【codevs3290】华容道
这题折腾了好几天,还跨度一次月考(直接咸鱼),最后做出了的时候钱包还不见了,心态booooooooom
这个题bfs+spfa,本来就很难想qwq,细节处理还贼麻烦,对比别人的ac程序还拍了半天,最后发现原来自己以为的一些可以放在后面的操作其实是不能放在后面的
不说了不说了,要说的全在代码里了QAQ,我要找钱包,还我钱包!!!!!
#include<iostream> #include<cstdio> #include<queue> using namespace std; const int mx=1000000007; struct in { int x,y,d,ans; }; queue<in>qwq,owo; int n,m,q,lin,fx[5]={0,-1,0,1,0},fy[5]={0,0,1,0,-1},op[5]={0,3,4,1,2}; int quan[35][35][5][5],ex,ey,sx,sy,tx,ty,dij[35][35][5],ans;//quan[i][j][k][l]表示(x,y)这个点从它向k方向上走了一步的点到它到l方向上走了一步的点的距离 bool mmp[35][35],vis[35][35],used[35][35][5];//used[i][j][k]表示从i到j且之前到达j之前的最后一步是向k方向 inline void yu1(int x,int y)//提前处理好空格向这个格子四个方向之间移动的距离 { for(int i=1;i<=4;i++)//枚举出发点方向 { if(!mmp[x+fx[i]][y+fy[i]])//如果这个点不能走 continue; for(int j=1;j<=4;j++)//枚举目标点的方向 { int xx=x+fx[j],yy=y+fy[j],tot=0;//tot用来记录答案 if(i==j||!mmp[xx][yy]||quan[x][y][i][j])//如果目标点不能走或者是目标格与当前格子重合或者之前走过 continue; for(int k=1;k<=n;k++)//初始化 for(int l=1;l<=m;l++) vis[k][l]=0; while(!owo.empty()) owo.pop(); owo.push((in){x+fx[i],y+fy[i],i,0}); vis[x+fx[i]][y+fy[i]]=1; tot=-1; while(!owo.empty()) { in wow=owo.front(); if(wow.x==xx&&wow.y==yy)//如果走到目标点 { tot=wow.ans;break; } for(int k=1;k<=4;k++)//否则就继续走 { int nx=wow.x+fx[k],ny=wow.y+fy[k]; if(k!=op[wow.d]&&!vis[nx][ny]&&mmp[nx][ny]&&(nx!=x||ny!=y)) owo.push((in){nx,ny,k,wow.ans+1}),vis[nx][ny]=1; } owo.pop(); } quan[x][y][i][j]=quan[x][y][j][i]=tot;//赋值 } } } inline void yu2()//每次处理空格到出发点的距离 { for(int i=1;i<=4;i++)//枚举出发点身边的四个格子 { int xx=sx+fx[i],yy=sy+fy[i]; if(!mmp[xx][yy]) continue; while(!owo.empty()) owo.pop(); int re=mx; for(int j=1;j<=n;j++) for(int k=1;k<=m;k++) vis[j][k]=0; owo.push((in){ex,ey,0,0}); vis[ex][ey]=1; while(!owo.empty()) { in wow=owo.front(); if(wow.x==xx&&wow.y==yy)//如果到达之前所枚举的格子 { re=wow.ans;break; } for(int j=1;j<=4;j++) { int nx=wow.x+fx[j],ny=wow.y+fy[j]; if(mmp[nx][ny]&&j!=op[wow.d]&&!vis[nx][ny]&&(nx!=sx||ny!=sy)) owo.push((in){nx,ny,j,wow.ans+1}),vis[nx][ny]=1; } owo.pop(); } if(re<mx) dij[xx][yy][i]=re+1,used[xx][yy][i]=1,qwq.push((in){xx,yy,i,0});//如果说能走到的话就把它作为初始状态之一加进去 } } inline void solve() { ans=1000000007; while(!qwq.empty()) qwq.pop(); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) for(int k=1;k<=4;k++) dij[i][j][k]=mx,used[i][j][k]=0;//dij[i][j][k]表示从i到j上一次走是向k方向 yu2(); if(tx==sx&&ty==sy)//如果起点等于终点要特判,不然会出错,因为并没有直接将起点作为初始状态 ans=0; while(!qwq.empty()) { in qaq=qwq.front();qwq.pop();used[qaq.x][qaq.y][qaq.d]=0;//记得这些操作全部要在前面完成,不加pop会死循,不清零会wa if(qaq.x==tx&&qaq.y==ty) { ans=min(ans,dij[qaq.x][qaq.y][qaq.d]);continue; } for(int i=1;i<=4;i++)//枚举下一步方向 { if(i!=op[qaq.d])//注意不能走上一步方向的反方向,这样又回去了。op[i]表示i的反方向 { int nx=qaq.x+fx[i],ny=qaq.y+fy[i],v=quan[qaq.x][qaq.y][op[qaq.d]][i]; if(v<=0||!mmp[nx][ny])//v<=0说明不能走 continue; if(dij[nx][ny][i]>dij[qaq.x][qaq.y][qaq.d]+1+v)//正常的spfa { dij[nx][ny][i]=dij[qaq.x][qaq.y][qaq.d]+1+v; if(!used[nx][ny][i]) used[nx][ny][i]=1,qwq.push((in){nx,ny,i,0}); } } } } if(ans>=mx||ans<0) ans=-1; printf("%d\n",ans); } int main() { scanf("%d%d%d",&n,&m,&q); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&lin),mmp[i][j]=lin; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) if(mmp[i][j]) yu1(i,j);//预处理 for(int i=1;i<=q;i++) scanf("%d%d%d%d%d%d",&ex,&ey,&sx,&sy,&tx,&ty),solve(); }