码迷,mamicode.com
首页 > 其他好文 > 详细

游戏:最短路,拆点

时间:2019-09-04 21:51:11      阅读:185      评论:0      收藏:0      [点我收藏+]

标签:pen   size   name   lap   spl   pac   pre   gif   bool   

把问题抽象成图论应该不难(也许都不用抽象?),但是怎么建边怎么跑就千差万别了。

首先应该注意到的一点是坐标的范围是0~500,也就是501*501个位置,所以数组/队列不要开小。

另外题目给出的莉露露没说位置不能重复,所以每个点可能不止入队一次,仍然要注意数组大小。

刚开始一直在想复杂度与n挂钩的算法,但是它挂了。

可以发现,n与x*y的差距并不打,所以如果正解复杂度可接受n的话那么拿xy作为复杂度应该没有问题。

先大概讲一下考场上的暴力(80分,因为上面说的锅,把数组开大是88分):

考虑每个莉露露的行动:去一个位置,捡起由岐,走几步,扔出去。

如果我们要对于每一个格子进行转移而不是针对每一个莉露露的话,去一个位置捡起由岐这个操作就可以变成:

找到最近的莉露露来这个位置,再让这个莉露露去走,扔。

走和扔的话就能比较简单的转移了。

那么首先的一个问题是,怎么找到离每一个点最近的莉露露有多远?

所有的二维几何题都可以用KD-tree乱搞。貌似的确可以这也是我考场上打了一半的思路。

但是这其实就是一个BFS,以每一个莉露露为源点不断扩散直至每个点都会被搜索到一次。

复杂度O(xy)。可以考虑建一个超级源点其距离值为-1,由它连向每一个莉露露,当然也可以直接搞。

然后假如我们已经找到了这个数组叫nst吧,那么怎么跑最短路?

我们假设由岐始终没着地:在被扔之前另一个莉露露已经到了落点去准备接住了。(当然不会影响答案)

那么她就始终在莉露露的手里了,所有的莉露露只有两种操作。

1.走一步,费用C,那么就想四周连边(不用真的建边直接for枚举就行)。连4条边。

2.扔。目的地只能是本行或本列上的,还是枚举,连了x+y条边。

而因为我们假设不让由岐落地,所以在到了目的地之后还要让最近的莉露露来接住她,费用就是ax+b+c*nst[tx][ty]

所以总边数是x3级别的,复杂度也是。

另外还可以优化一下,如果你是被从这一列扔过来的,那么你不会再被扔到这一列,行同理,开一个bool数组更新最小距离时记录就好。

这样的话边数能减少一些,具体多少看测试点,反正效果还可以。

技术图片
 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cstring>
 4 #include<queue>
 5 using namespace std;
 6 const int xx[]={1,0,0,-1},yy[]={0,1,-1,0};
 7 #define tx qx[h]+xx[i]
 8 #define ty qy[h]+yy[i]
 9 struct ps{int x,y;long long d;friend bool operator<(ps a,ps b){return a.d>b.d;}};
10 priority_queue<ps>q;
11 bool l[505][505],r[505][505];
12 int com,nst[505][505],X,Y,n,qx[500155],qy[500155],gx,gy,sx,sy;
13 long long a,b,c,dt[505][505];
14 signed main(){//freopen("ex_game5.in","r",stdin);
15     memset(nst,0x3f,sizeof nst);memset(dt,0x3f,sizeof dt);
16     scanf("%d%d%lld%lld%lld%d%d%d",&X,&Y,&a,&b,&c,&n,&sx,&sy);
17     nst[sx][sy]=0,qx[1]=sx,qy[1]=sy;
18     for(int i=2;i<=n;++i)scanf("%d%d",&gx,&gy),nst[gx][gy]=0,qx[i]=gx,qy[i]=gy;
19     for(int h=1,t=n;h<=t;++h)for(int i=0;i<=3;++i)
20         if(tx>=0&&ty>=0&&tx<=X&&ty<=Y&&nst[tx][ty]>nst[qx[h]][qy[h]]+1)
21             ++t,nst[qx[t]=tx][qy[t]=ty]=nst[qx[h]][qy[h]]+1;//,printf("%lld %lld\n",sx,sy);
22     q.push((ps){sx,sy,dt[sx][sy]=0});
23     while(!q.empty()){
24         int x=q.top().x,y=q.top().y;long long d=q.top().d;q.pop();
25         if(dt[x][y]!=d)continue;
26         if(x==gx&&y==gy){printf("%lld\n",d);return 0;}
27         #define kx x+xx[i]
28         #define ky y+yy[i]
29         for(int i=0;i<=3;++i)if(kx>=0&&kx<=X&&ky<=Y&&ky>=0&&dt[kx][ky]>d+c)
30             l[kx][ky]=r[kx][ky]=0,q.push((ps){kx,ky,dt[kx][ky]=d+c});
31         if(!l[x][y])for(int i=0;i<x;++i)if(dt[i][y]>d+a*(x-i)+b+c*nst[i][y])
32             l[i][y]=1,r[i][y]=0,q.push((ps){i,y,dt[i][y]=d+a*(x-i)+b+c*nst[i][y]});
33         if(!l[x][y])for(int i=x+1;i<=X;++i)if(dt[i][y]>d+a*(i-x)+b+c*nst[i][y])
34             l[i][y]=1,r[i][y]=0,q.push((ps){i,y,dt[i][y]=d+a*(i-x)+b+c*nst[i][y]});
35         if(!r[x][y])for(int i=0;i<y;++i)if(dt[x][i]>d+a*(y-i)+b+c*nst[x][i])
36             r[x][i]=1,l[x][i]=0,q.push((ps){x,i,dt[x][i]=d+a*(y-i)+b+c*nst[x][i]});
37         if(!r[x][y])for(int i=y+1;i<=Y;++i)if(dt[x][i]>d+a*(i-y)+b+c*nst[x][i])
38             r[x][i]=1,l[x][i]=0,q.push((ps){x,i,dt[x][i]=d+a*(i-y)+b+c*nst[x][i]});
39     }
40 }
View Code

100%算法就是稍微优化了一下建边。

稍微改变状态定义,其实每次被扔出去可以被分为两个阶段,从那个ax+b能yy一下。

首先花费b的费用起飞,然后沿着一个方向飞,每飞一个格子的费用是a,着陆的费用就是最近的莉露露捡起她的费用c×nst。

当然也可以不飞,一步一步走,这里和暴力是一样的。

那么因为被扔的操作每次也只会向上下左右四个格子转移了,所以复杂度有了保证。

然而你现在需要弄清楚她有没有起飞有没有着陆,如果在飞的话她是在横着飞还是竖着。

拆点,拆成3个,分别表示0在地上,1在横着飞,2在竖着飞。

可以从0转移到1和2,费用为b。

可以从1,2转移到0,费用为c×nst。

可以从0转移到相邻4个格子的0,费用为c。

可以从1转移到左右两个格子的1,费用为a。

可以从2转移到上下两个格子的2,费用为a。

总边数10n2。跑Dijkstra,复杂度乘个log,问题不大。

据secret测试可以暴力把所有边都建出来也能A,但是不推荐大常数者使用。

技术图片
 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cstring>
 4 #include<queue>
 5 #include<ctime>
 6 using namespace std;
 7 const int xx[]={1,-1,0,0},yy[]={0,0,-1,1};
 8 #define tx qx[h]+xx[i]
 9 #define ty qy[h]+yy[i]
10 struct ps{int x,y,st;long long d;friend bool operator<(ps a,ps b){return a.d>b.d;}};
11 priority_queue<ps>q;
12 int com,X,Y,n,qx[500155],qy[500155],gx,gy,sx,sy;
13 long long a,b,c,dt[3][505][505],nst[505][505];
14 signed main(){//freopen("ex_game5.in","r",stdin);
15     memset(nst,0x3f,sizeof nst);memset(dt,0x3f,sizeof dt);
16     scanf("%d%d%lld%lld%lld%d%d%d",&X,&Y,&a,&b,&c,&n,&sx,&sy);
17     nst[sx][sy]=0,qx[1]=sx,qy[1]=sy;
18     for(int i=2;i<=n;++i)scanf("%d%d",&gx,&gy),nst[gx][gy]=0,qx[i]=gx,qy[i]=gy;
19     for(int h=1,t=n;h<=t;++h)for(int i=0;i<=3;++i)
20         if(tx>=0&&ty>=0&&tx<=X&&ty<=Y&&nst[tx][ty]>nst[qx[h]][qy[h]]+c)
21             nst[qx[++t]=tx][qy[t]=ty]=nst[qx[h]][qy[h]]+c;
22     q.push((ps){sx,sy,0,dt[0][sx][sy]=0});
23     while(!q.empty()){
24         int x=q.top().x,y=q.top().y,st=q.top().st;long long d=q.top().d;q.pop();
25         if(dt[st][x][y]!=d)continue;
26         if(x==gx&&y==gy){printf("%lld\n",d);return 0;}
27         #define kx x+xx[i]
28         #define ky y+yy[i]
29         if(!st){
30             for(int i=0;i<=3;++i)if(kx>=0&&kx<=X&&ky<=Y&&ky>=0&&dt[0][kx][ky]>d+c)
31                 q.push((ps){kx,ky,0,dt[0][kx][ky]=d+c});
32             for(int s=1;s<=2;++s)if(dt[s][x][y]>d+b)
33                 q.push((ps){x,y,s,dt[s][x][y]=d+b});
34         }else{
35             for(int i=st*2-2;i<=st*2-1;++i)if(kx>=0&&kx<=X&&ky<=Y&&ky>=0&&dt[st][kx][ky]>d+a)
36                 q.push((ps){kx,ky,st,dt[st][kx][ky]=d+a});
37             if(dt[0][x][y]>d+nst[x][y])q.push((ps){x,y,0,dt[0][x][y]=d+nst[x][y]});
38         }
39     }
40 }
代码复杂度不大,1.5k

 

游戏:最短路,拆点

标签:pen   size   name   lap   spl   pac   pre   gif   bool   

原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/11461203.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!