游戏:最短路,拆点
把问题抽象成图论应该不难(也许都不用抽象?),但是怎么建边怎么跑就千差万别了。
首先应该注意到的一点是坐标的范围是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 }
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 }