[NOIP2013] 华容道
初看这道题,想到纯暴力bfs , 但是套一个并查集优化 -1 的情况
但是要特判 出发点 与 目的点 重合的情况!!!!!! (score 10)
TLE&WA 代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<vector> #include<queue> #include<stack> #include<map> #include<set> #include<string> #include<iomanip> #include<ctime> #include<climits> #include<cctype> #include<algorithm> #ifdef WIN32 #define AUTO "%I64d" #else #define AUTO "%lld" #endif using namespace std; #define smin(x,tmp) x=min(x,tmp) #define smax(x,tmp) x=max(x,tmp) const int maxn=35; const int INF=0x3f3f3f3f; const int dx[]={0,-1,0,1,0}; const int dy[]={0,0,-1,0,1};//u l d r int n,m,q; int g[maxn][maxn]; struct Coord { int x,y; bool operator == (const Coord t)const { return x==t.x&&y==t.y; } }; Coord fa[maxn][maxn]; Coord find(int x,int y) { if(fa[x][y]==(Coord){x,y}) return fa[x][y]; else return fa[x][y]=find(fa[x][y].x,fa[x][y].y); } inline bool union_find(int x,int y,int i,int j) { Coord t1=find(x,y),t2=find(i,j); if(t1==t2) return false; fa[t2.x][t2.y]=t1; return true; } inline 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]),fa[i][j]=(Coord){i,j}; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) for(int d=1;d<=2;d++) { int x=i+dx[d],y=j+dy[d]; union_find(x,y,i,j); } } struct Status { int ei,ej; int ci,cj; Status (const int e1,const int e2,const int c1,const int c2) { ei=e1,ej=e2; ci=c1,cj=c2; } bool check(int di,int dj) { return ci==di && cj==dj; } bool operator < (const Status t) const { if(ei^t.ei) return ei<t.ei; if(ej^t.ej) return ej<t.ej; if(ci^t.ci) return ci<t.ci; return cj<t.cj; } }; int f[maxn][maxn][maxn][maxn];//no need to use map!! int bfs(Status S,int di,int dj) { if(S.ei == di && S.ej == dj) return 0;// must judge here!! same node!! queue <Status> que; que.push(S);f[S.ei][S.ej][S.ci][S.cj]=0; while(!que.empty()) { Status u=que.front();que.pop(); int x0=u.ei,y0=u.ej; for(int i=1;i<=4;i++) { int x=x0+dx[i],y=y0+dy[i]; if(!g[x][y]) continue; Status v=u; v.ei=x,v.ej=y; if(x==v.ci && y==v.cj) v.ci=u.ei,v.cj=u.ej; int cost=f[u.ei][u.ej][u.ci][u.cj]+1; if(v.check(di,dj)) return cost; if(!f[v.ei][v.ej][v.ci][v.cj] || f[v.ei][v.ej][v.ci][v.cj]>cost) f[v.ei][v.ej][v.ci][v.cj]=cost,que.push(v); } } return -1; } int main() { freopen("puzzle.in","r",stdin); freopen("puzzle.out","w",stdout); init(); while(q--) { int e1,e2,c1,c2,d1,d2; scanf("%d%d%d%d%d%d",&e1,&e2,&c1,&c2,&d1,&d2); if(find(e1,e2)==find(d1,d2)&&find(d1,d2)==find(c1,c2)) printf("%d\n",bfs(Status(e1,e2,c1,c2),d1,d2)); else printf("-1\n"); memset(f,0,sizeof(f)); } return 0; }
现考虑正解
由于每次移动都需要把空格移动到当前点四周
可以用 n2 次 O(n2) 的 bfs() 预处理出所有相邻情况
step[i][j][k][h] 表示当前点在 (i,j) 空格点在当前点的 k 方向上 ,并且要把空格移动到 h 方向上的最小步数
并且可以把当前点扣掉进行bfs
然后查询时分成从初始空格点到出发点 以及 当前点到目的点的两段!!!
AC代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<vector> #include<queue> #include<stack> #include<map> #include<set> #include<string> #include<iomanip> #include<ctime> #include<climits> #include<cctype> #include<algorithm> #ifdef WIN32 #define AUTO "%I64d" #else #define AUTO "%lld" #endif using namespace std; #define smin(x,tmp) x=min(x,tmp) #define smax(x,tmp) x=max(x,tmp) const int maxn=35; const int INF=0x3f3f3f3f; const int dx[]={-1,1,0,0}; const int dy[]={0,0,-1,1};//u d l r, u^1=d int n,m,q; int g[maxn][maxn]; int step[maxn][maxn][4][4];//i j, with the blank on the k side and l direction to move,but the blank node is to the opposite side, with which the target un moved!! struct Coord { int x,y; Coord (const int x0,const int y0) { x=x0;y=y0; } bool operator == (const Coord t) const { return x==t.x && y==t.y; } }; inline bool inside(int x,int y) { if(!g[x][y]) return false; return x>=1 && x<=n && y>=1 && y<=m ? true : false; } int d[maxn][maxn];//for bfs int bfs(int si,int sj,int ti,int tj) { if(!inside(si,sj) || !inside(ti,tj)) return INF; if(si==ti && sj==tj) return 0; queue <Coord> que; memset(d,0x3f,sizeof(d)); d[si][sj]=0; que.push(Coord(si,sj)); while(!que.empty()) { int x0=que.front().x,y0=que.front().y;que.pop(); for(int k=0;k^4;k++) { int x=x0+dx[k],y=y0+dy[k]; if(!inside(x,y)) continue; if(d[x][y]>d[x0][y0]+1) { d[x][y]=d[x0][y0]+1; que.push(Coord(x,y)); if(d[ti][tj]^INF) return d[ti][tj]; } } } return INF; } inline 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]); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { int tmp=g[i][j];g[i][j]=0;// guarantee that no going though the target node!! BUT!!!!! MUST depend on its previous value!!! for(int k=0;k^4;k++) for(int l=0;l^4;l++) step[i][j][k][l]=bfs(i+dx[k],j+dy[k],i+dx[l],j+dy[l]); g[i][j]=tmp;//recovery } } int ei,ej,si,sj,ti,tj; int dis[maxn][maxn][4];//for spfa(generally), from the point of(si,sj), with the function of judge vis!! bool inque[maxn][maxn][4]; struct Node { int x,y; int k; Node (const int x0,const int y0,const int k0) { x=x0,y=y0,k=k0; } }; int spfa(int si,int sj,int ti,int tj) { queue <Node> que; for(int k=0;k^4;k++) if(inside(si+dx[k],sj+dy[k])) que.push(Node(si,sj,k)),inque[si][sj][k]=true;// status must be the STARTing Node not the blank space node!!! while(!que.empty()) { int x0=que.front().x,y0=que.front().y,k0=que.front().k;que.pop();inque[x0][y0][k0]=false; for(int k=0;k^4;k++) { int x=x0+dx[k],y=y0+dy[k]; if(!inside(x,y)) continue; if(dis[x][y][k^1] > dis[x0][y0][k0] + step[x0][y0][k0][k] +1) { dis[x][y][k^1] = dis[x0][y0][k0] + step[x0][y0][k0][k] +1; // last status to the current status, and +1 to make the blank space exhanged to the opposite position!! if(!inque[x][y][k^1]) que.push(Node(x,y,k^1)) , inque[x][y][k^1]=true; } } } int ans = INF; for(int k=0;k^4;k++)// dont hand damn!! smin(ans,dis[ti][tj][k]); return ans^INF?ans:-1;//no need to +1 coz the ti tj is already here!! } int work() { scanf("%d%d%d%d%d%d",&ei,&ej,&si,&sj,&ti,&tj); if(si==ti && sj==tj) return 0; if(!inside(si,sj) || !inside(ti,tj)) return INF; memset(dis,0x3f,sizeof(dis)); int tmp=g[si][sj];g[si][sj]=0;// uncertain of its previous value!! for(int k=0;k^4;k++) if(g[si+dx[k]][sj+dy[k]]) dis[si][sj][k]=bfs(ei,ej,si+dx[k],sj+dy[k]);//from the start empty node to the START node g[si][sj]=tmp; return spfa(si,sj,ti,tj); } int main() { freopen("puzzle.in","r",stdin); freopen("puzzle.out","w",stdout); init(); while(q--) printf("%d\n",work()); return 0; }