【NOIP2013提高组day2】华容道

Description

给出一张n*m的棋盘,有一些点上有障碍物,其他点上都是棋子。给出q次询问,每次询问给出一个空格,一个目标棋子,一个目标位置,每一步可以把一个棋子移进空格,求把目标棋子移动到目标位置的最小步数。
n,m<=30,q<=500

Solution

第一眼看到就知道是大暴力题,发现询问次数少的时候一次bfs就解决了,那么询问多起来呢?
预处理!
如何预处理?
我们先不想这个问题,想一想询问少的时候的另一种做法,DP。
设Fx1,y1,x2,y2表示空格在(x1,y1),目标棋子在(x2,y2)的最小步数,转移显然。
我们发现,在整个DP过程中x1,y1经常改变,x2,y2却很少改变。
那我们可以改进一下DP,设Fx,y,k(k=0..3)表示目标棋子在(x,y),空格在其上(下,左,右)的最小步数。
怎么转移?
发现这样很难转移,不如把每一个状态抽象成一个点,然后向它所能转移到的状态连边。边权可以用N^2的bfs暴力搞出来。一个状态可以有两种后继。一种是把空格和目标棋子交换,边权为1;另一种就是把空格换个位置,边权自己暴力。
发现这样建出来的图和那些询问的东西无关,就可以预处理出来了。(∩_∩)
然后,对于每次询问,建立源点和汇点。(网络流打多了)从源点向每一个目标棋子周围的状态连边,边权暴力^2,每一个终止状态向汇点连边,边权为0。
然后就可以跑一边spfa 费用流来求答案了。时间飞起。
(原谅我网络流写太多了,有些神经质)

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define N 35
#define M 4005
#define inf 0x7fffffff/3
using namespace std;
int n,m,q,x,y,s,sx,sy,tx,ty,xx,yy,ret,ans,tot,l,S,T;
int a[N][N],d[M][3],dis[M],p[M*4],v[M][M],b[M][M];
bool bz[N][N],f[M],r[4];
int g[4][2]={-1,0,1,0,0,1,0,-1};
void add(int x,int y,int z) {b[x][++b[x][0]]=y;v[x][y]=z;}
int w(int x,int y,int z) {return z*n*m+(x-1)*m+y;}
int bfs(int sx,int sy,int tx,int ty,int x,int y) {
 memset(bz,0,sizeof(bz));
 int i=0,j=1;d[1][0]=sx;d[1][1]=sy;d[1][2]=0;bz[sx][sy]=1;
 if (sx==tx&&sy==ty) return 0;
 while (i<j) {
 int px=d[++i][0],py=d[i][1];
 fo(k,0,3) {
 int dx=px+g[k][0],dy=py+g[k][1];
 if (dx<1||dx>n||dy<1||dy>m||!a[dx][dy]
 ||bz[dx][dy]
 ||(dx==x&&dy==y)) continue;
 d[++j][0]=dx;d[j][1]=dy;d[j][2]=d[i][2]+1;bz[dx][dy]=1;
 if (dx==tx&&dy==ty) return d[j][2];
 }
 }
 return -1;
}
int spfa() {
 memset(dis,127,sizeof(dis));
 int mx=dis[S];dis[S]=0;
 memset(f,0,sizeof(f));
 f[S]=1;p[1]=S;int i=0,j=1;
 while (i<j) {
 int x=p[++i];
 fo(k,1,b[x][0]) if (dis[b[x][k]]>dis[x]+v[x][b[x][k]]) {
 dis[b[x][k]]=dis[x]+v[x][b[x][k]];
 if (!f[b[x][k]]) f[b[x][k]]=1,p[++j]=b[x][k];
 }
 f[p[i]]=0;
 }
 if (dis[T]!=mx) return dis[T];else return -1;
}
int main() { 
 scanf("%d%d%d",&n,&m,&q);
 fo(i,1,n)
 fo(j,1,m) scanf("%d",&a[i][j]);
 fo(i,1,n)
 fo(j,1,m)
 if (a[i][j]) 
 fo(k,0,3) {
 xx=i+g[k][0],yy=j+g[k][1];
 if (xx<1||xx>n||yy<1||yy>m||!a[xx][yy]) continue;
 if (k%2) s=k-1;else s=k+1;
 add(w(i,j,k),w(xx,yy,s),1);
 fo(s,0,3) {
 if (s==k) continue;
 int dx=i+g[s][0],dy=j+g[s][1];
 if (dx<1||dx>n||dy<1||dy>m||!a[dx][dy]) continue;
 ret=bfs(xx,yy,dx,dy,i,j);
 if (ret==-1) continue;
 add(w(i,j,k),w(i,j,s),ret);
 }
 }
 S=0;T=4*n*m+1;
 while (q) {
 q--;scanf("%d%d%d%d%d%d",&x,&y,&sx,&sy,&tx,&ty);
 if (sx==tx&&sy==ty) {printf("0\n");continue;}
 fo(i,0,3) {
 xx=sx+g[i][0],yy=sy+g[i][1];
 if (xx<1||xx>n||yy<1||yy>m||!a[xx][yy]) continue;
 ret=bfs(x,y,xx,yy,sx,sy);
 if (ret==-1) continue;
 add(S,w(sx,sy,i),ret);
 }
 fo(i,0,3) {
 xx=tx+g[i][0],yy=ty+g[i][1];
 if (xx<1||xx>n||yy<1||yy>m||!a[xx][yy]) continue;
 add(w(tx,ty,i),T,0);r[i]=1;
 } 
 printf("%d\n",spfa()); 
 b[S][0]=0;fo(i,0,3) if (r[i]) r[i]=0,b[w(tx,ty,i)][0]--; 
 }
}

为什么变黑了?!(TAT)

你可能感兴趣的:(最短路,暴力,华容道,NOIp2013,提高组day2)