【题解】P1979 华容道 (Noip2013)
Description
小 B 最近迷上了华容道,可是他总是要花很长的时间才能完成一次。于是,他想到用编程来完成华容道:给定一种局面, 华容道是否根本就无法完成,如果能完成, 最少需要多少时间。
小 B 玩的华容道与经典的华容道游戏略有不同,游戏规则是这样的
在一个 \(n×mn \times mn×m\) 棋盘上有\(n \times m\) 个格子,其中有且只有一个格子是空白的,其余 \(n×m−1\) 个格子上每个格子上有一个棋子,每个棋子的大小都是 \(1 \times 1\) 的;
有些棋子是固定的,有些棋子则是可以移动的;
任何与空白的格子相邻(有公共的边)的格子上的棋子都可以移动到空白格子上。
游戏的目的是把某个指定位置可以活动的棋子移动到目标位置。
给定一个棋盘,游戏可以玩 \(q\) 次,当然,每次棋盘上固定的格子是不会变的, 但是棋盘上空白的格子的初始位置、 指定的可移动的棋子的初始位置和目标位置却可能不同。第 \(i\) 次玩的时候, 空白的格子在第 \(EXi\) 行第 \(EYi\) 列,指定的可移动棋子的初始位置为第 \(SXi\) 行第 \(SYi\), \(TXi\) 行第 \(TYi\) 列。
假设小 B 每秒钟能进行一次移动棋子的操作,而其他操作的时间都可以忽略不计。请你告诉小 B 每一次游戏所需要的最少时间,或者告诉他不可能完成游戏。
Sample Input
3 4 2
0 1 1 1
0 1 1 0
0 1 0 0
3 2 1 2 2 2
1 2 2 2 3 2
Sample Output
2
-1
Hint
\(1 \leqslant x,y \leqslant 30\)
\(q \leqslant 500\)
Solution
看了一个上午,果然是道Day2T2,够恶心!!!
写篇题解记录一下心酸的历史。
这题考虑直接暴力的做法:
我们发现直接直接按照题意模拟有点麻烦,找找规律,灵机一动忽然想到如果空格子和别的格子交换了位置,那么他们是不是就是等于空格子的位置改变了?
废话!
那么可以考虑移动空格子,让他先去到棋子的其实位置旁边然后一步步让空格子带着这个棋子走。具体没写过,可以参考这份代码
Brute Force
inline void dfs (ll x, ll y, ll lx, ll ly, ll Step) {
if (Step >= Ans) return ;
if (lx == tx && ly == ty) return (void) (Ans = Step);
if (Vis[x][y][lx][ly] <= Step) return ; Vis[x][y][lx][ly] = Step;
for (int i = 0; i < 4; ++i) {
ll nx = x + dx[i], ny = y + dy[i];
if (!mp[nx][ny]) continue;
if (nx <= 0 || nx > n || ny <= 0 || ny > m) continue;
if (nx == lx && ny == ly) dfs (nx, ny, x, y, Step);
if (nx != lx || ny != ly) dfs (nx, ny, lx, ly, Step);
}
}
考虑正解,其实是刚刚已经差不多想出来了,发现刚刚其实就有一点浪费在把空格移到目标格子的旁边,那么我们可不可以优化一下,直接把不同状态之间连边,设计一个状态 \(dis[i][j]\) 表示把空格移到 \(i,j\) 这个位置的最小步数,然后我们对于每个点,求出如果空格在他的旁边的某个位置,并且不经过当前这个点到达这个点另一个状态的最小步数。
预处理完之后再用一遍bfs算出给出的空格到达给出棋子初始位置的旁边的最小步数
最后用spfa算出空格到结束位置的旁边的最小步数。
为什么是结束旁边? 因为空格在终点上时初始棋子才可以和他交换,交换后就到旁边去了。
Real Solution
#include<bits/stdc++.h>
using namespace std;
#define A first
#define B second
int n,m,q,En;
const int N=35,M=5005,INF=0x3f3f3f3f;
const int dx[]={-1,0,1,0};
const int dy[]={0,1,0,-1};
int head[M],dis[N][N],d[M],G[N][N];
struct edge {
int next,to,dis;
}E[M<<3];
bool vis[M];
inline void AddEdge(int from,int to,int dis){
E[++En].next=head[from];
E[En].to=to;
E[En].dis=dis;
head[from]=En;
}
inline int GetId(int x,int y){
return (x-1)*m*4+(y-1)*4;
}
inline void bfs(int ex,int ey,int sx,int sy,int dir){
memset(dis,-1,sizeof(dis));
dis[sx][sy]=1;dis[ex][ey]=0;
queue < pair<int,int> > q;
q.push(make_pair(ex,ey));
while(!q.empty()){
int cx=q.front().A;
int cy=q.front().B;
q.pop();
for(int i=0;i<4;++i){
int x=dx[i]+cx,y=dy[i]+cy;
if(x<1 || x>n || y<1 || y>m || !G[x][y] || dis[x][y]!=-1) continue;
dis[x][y]=dis[cx][cy]+1;
q.push(make_pair(x,y));
}
}
if(dir==4) return;
int id=GetId(sx,sy);
for(int i=0;i<4;++i)
if(dis[sx+dx[i]][sy+dy[i]]>0)
AddEdge(id+dir,id+i,dis[sx+dx[i]][sy+dy[i]]);
AddEdge(id+dir,GetId(ex,ey)+(dir+2)%4,1);
}
inline void spfa(int sx,int sy){
memset(d,0x3f,sizeof(d));
memset(vis,0,sizeof(vis));
queue <int> q;
int id=GetId(sx,sy);
for(int i=0;i<4;++i){
if(dis[sx+dx[i]][sy+dy[i]]!=-1){
q.push(i+id);
vis[i+id]=true;
d[i+id]=dis[sx+dx[i]][sy+dy[i]];
}
}
while(!q.empty()){
int u=q.front(); q.pop();
vis[u]=false;
for(int i=head[u];i;i=E[i].next){
int v=E[i].to;
if(d[v]>d[u]+E[i].dis){
d[v]=d[u]+E[i].dis;
if(!vis[v]){
vis[v]=true;
q.push(v);
}
}
}
}
}
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]);
for(int i=1;i<=n;++i) for(int j=1;j<=m;++j)
if(G[i][j]) for(int k=0;k<4;++k){
int x=i+dx[k],y=j+dy[k];
if(G[x][y]) bfs(x,y,i,j,k);
}
while(q--){
int sx=0,sy=0,ex=0,ey=0,tx=0,ty=0;
scanf("%d%d%d%d%d%d",&ex,&ey,&sx,&sy,&tx,&ty);
if(sx==tx && sy==ty){
puts("0");
continue;
}
bfs(ex,ey,sx,sy,4);
spfa(sx,sy);
int id=GetId(tx,ty);
int ans=INF;
for(int i=0;i<4;++i) ans=min(ans,d[id+i]);
printf("%d\n",(ans==INF)?-1:ans);
}
return 0;
}
```【】