noip 2013 华容道
/*双向bfs (得分和单项的一样多....)70*/ #include<iostream> #include<cstdio> #include<cstring> #include<queue> #define maxn 35 #define maxm 810010 using namespace std; int n,m,q,g[maxn][maxn]; int X,Y,sx,sy,ex,ey,falg,vis[maxn][maxn]; int xx[4]={0,0,1,-1}; int yy[4]={1,-1,0,0}; int f[maxn][maxn][maxn][maxn],s[maxn][maxn][maxn][maxn]; struct node { int kx,ky,xi,yi; node (int xxx, int yyy, int tt, int ss):kx(xxx), ky(yyy), xi(tt), yi(ss){}; }; queue<node>Q; void Bfs() { if(sx==ex&&sy==ey) { printf("0\n"); falg=1;return; } while(!Q.empty())Q.pop(); f[X][Y][sx][sy]=1; s[X][Y][sx][sy]=0; Q.push(node(X,Y,sx,sy)); for(int i=0;i<4;i++) { int nx=xx[i]+ex; int ny=yy[i]+ey; if(nx>0&&nx<=n&&ny>0&&ny<=m&&g[nx][ny]&&f[nx][ny][ex][ey]==0) { f[nx][ny][ex][ey]=2; s[nx][ny][ex][ey]=0; Q.push(node(nx,ny,ex,ey)); } } while(!Q.empty()) { node t=Q.front();Q.pop(); int x=t.kx,y=t.ky; int xs=t.xi,ys=t.yi; int T=f[x][y][xs][ys]; int k=s[x][y][xs][ys]; for(int i=0;i<4;i++) { int nx=x+xx[i]; int ny=y+yy[i]; xs=t.xi;ys=t.yi; if(nx>0&&nx<=n&&ny>0&&ny<=m&&g[nx][ny]) { if(nx==xs&&ny==ys)xs=x,ys=y; if(f[nx][ny][xs][ys]==0) { f[nx][ny][xs][ys]=T; s[nx][ny][xs][ys]=k+1; Q.push(node(nx,ny,xs,ys)); } else if(f[nx][ny][xs][ys]!=T) { printf("%d\n",s[nx][ny][xs][ys]+k+1); falg=1;return; } } } } } 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",&g[i][j]); while(q--) { scanf("%d%d%d%d%d%d",&X,&Y,&sx,&sy,&ex,&ey); falg=0;memset(f,0,sizeof(f));Bfs(); if(falg==0)printf("-1\n"); } return 0; }
/* 后来看了别人的思路 机智啊! 本来bfs不超时的 nm*nm 但是加上q次询问就T了 问题就在这 我们重复的bfs太多了 能不能一次预处理好呢 但是考虑到每次的起点终点还有空格位置是在变化的 所以我们要与处理一些与这些无关的 但是对答案还有贡献的 考虑到目标棋子的移动必须先让空格移动到4周 所以我们把每个空格和目标棋子的相邻的组合作为状态 也就是有n*m*4个状态 每个状态不是相邻的 互相转移需要一定的步数 这个步数就使我们要预处理的东西 然后把每个状态映射成一个图 将目标棋盘转化成同样的状态 然后跑最短路 具体的细节看代码把 */ #include<iostream> #include<cstdio> #include<cstring> #include<queue> #define maxn 35 #define maxm 810010 using namespace std; int n,m,q,g[maxn][maxn],num,head[maxm],kx,ky,sx,sy,ex,ey; int f[maxn][maxn],dis[maxm],vis[maxm],ans; struct node{int u,v,t,pre;}e[maxm]; int xx[4]={-1,1,0,0};//上下左右 和下面的对应 int yy[4]={0,0,-1,1}; void Init() { scanf("%d%d%d",&n,&m,&q); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&g[i][j]); } void Add(int from,int to,int dis) { num++;e[num].v=to; e[num].t=dis; e[num].pre=head[from]; head[from]=num; } void Bfs(int X,int Y,int xi,int yi,int k) { queue<int>qx,qy; while(!qx.empty())qx.pop(); while(!qy.empty())qy.pop(); memset(f,0,sizeof(f)); qx.push(xi);qy.push(yi); f[xi][yi]=1;//判重+步数记录 while(!qx.empty()) { int x=qx.front();qx.pop(); int y=qy.front();qy.pop(); for(int i=0;i<4;i++) { int nx=x+xx[i]; int ny=y+yy[i]; if(nx==X&&ny==Y)continue; if(nx>0&&nx<=m&&ny>0&&ny<=m&&g[nx][ny]&&f[nx][ny]==0) { qx.push(nx);qy.push(ny); f[nx][ny]=f[x][y]+1; } } } if(k==4)return; for(int i=0;i<4;i++) { int nx=X+xx[i],ny=Y+yy[i]; if(f[nx][ny]==0||(nx==xi&&ny==yi))continue; Add(X*30*4+Y*4+k,X*30*4+Y*4+i,f[nx][ny]-1);//一一映射 } Add(X*30*4+Y*4+k,xi*30*4+yi*4+(k^1),1);//每组之间的状态连起来 这里的^ 就是左变变 上变下 } void Ready() { for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { if(!g[i][j]) continue; if(g[i-1][j]) Bfs(i,j,i-1,j,0); if(g[i+1][j]) Bfs(i,j,i+1,j,1); if(g[i][j-1]) Bfs(i,j,i,j-1,2); if(g[i][j+1]) Bfs(i,j,i,j+1,3); } } void SPFA() { queue<int>q;memset(dis,127/3,sizeof(dis)); for(int i=0;i<4;i++) { int nx=sx+xx[i]; int ny=sy+yy[i]; if(nx>0&&nx<=n&&ny>0&&ny<=m&&g[nx][ny]&&f[nx][ny]) { int k=sx*30*4+sy*4+i; q.push(k);vis[k]=1;dis[k]=f[nx][ny]-1;//转化成可表示的状态 } } while(!q.empty()) { int k=q.front();q.pop();vis[k]=0; for(int i=head[k];i;i=e[i].pre) if(dis[e[i].v]>dis[k]+e[i].t) { dis[e[i].v]=dis[k]+e[i].t; if(vis[e[i].v]==0) { vis[e[i].v]=1;q.push(e[i].v); } } } } int main() { Init(); Ready(); for(int i=1;i<=q;i++) { scanf("%d%d%d%d%d%d",&kx,&ky,&sx,&sy,&ex,&ey); if(sx==ex&&sy==ey) { printf("0\n");continue; } Bfs(sx,sy,kx,ky,4);//把空格移到目标棋子附近 SPFA();ans=dis[0]; for(int i=0;i<4;i++) { int k=ex*30*4+ey*4+i; ans=min(ans,dis[k]); } if(ans==dis[0])printf("-1\n"); else printf("%d\n",ans); } return 0; }