NOIP2014华容道
说起来这道题还挺有难度的,我用了两个小时才把它AC,要是在赛场上的话。。。。这种题就果断放弃了
下面步入正题
小 B 最近迷上了华容道,可是他总是要花很长的时间才能完成一次。于是,他想到用编程来完成华容道:给定一种局面,华容道是否根本就无法完成,如果能完成,最少需要多少时间。
小 B 玩的华容道与经典的华容道游戏略有不同,游戏规则是这样的:
- 在一个 n*m 棋盘上有 n*m 个格子,其中有且只有一个格子是空白的,其余 n*m-1个格子上每个格子上有一个棋子,每个棋子的大小都是 1*1 的;
- 有些棋子是固定的,有些棋子则是可以移动的;
- 任何与空白的格子相邻(有公共的边)的格子上的棋子都可以移动到空白格子上。 游戏的目的是把某个指定位置可以活动的棋子移动到目标位置。
给定一个棋盘,游戏可以玩 q 次,当然,每次棋盘上固定的格子是不会变的,但是棋盘上空白的格子的初始位置、指定的可移动的棋子的初始位置和目标位置却可能不同。第 i 次玩的时候,空白的格子在第 EX_i 行第 EY_i 列,指定的可移动棋子的初始位置为第 SX_i 行第 SY_i 列,目标位置为第 TX_i 行第 TY_i 列。
假设小 B 每秒钟能进行一次移动棋子的操作,而其他操作的时间都可以忽略不计。请你告诉小 B 每一次游戏所需要的最少时间,或者告诉他不可能完成游戏。
第一行有 3 个整数,每两个整数之间用一个空格隔开,依次表示 n、m 和 q;
接下来的 n 行描述一个 n*m 的棋盘,每行有 m 个整数,每两个整数之间用一个空格隔开,每个整数描述棋盘上一个格子的状态,0 表示该格子上的棋子是固定的,1 表示该格子上的棋子可以移动或者该格子是空白的。
接下来的 q 行,每行包含 6 个整数依次是 EX_i、EY_i、SX_i、SY_i、TX_i、TY_i,每两个整数之间用一个空格隔开,表示每次游戏空白格子的位置,指定棋子的初始位置和目标位置。
输出有 q 行,每行包含 1 个整数,表示每次游戏所需要的最少时间,如果某次游戏无法完成目标则输出-1。
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
2
-1
【样例说明】
棋盘上划叉的格子是固定的,红色格子是目标位置,圆圈表示棋子,其中绿色圆圈表示目标棋子。
第一次游戏,空白格子的初始位置是 (3, 2)(图中空白所示),游戏的目标是将初始位置在(1, 2)上的棋子(图中绿色圆圈所代表的棋子)移动到目标位置(2, 2)(图中红色的格子)上。
移动过程如下:
第二次游戏,空白格子的初始位置是(1, 2)(图中空白所示),游戏的目标是将初始位置在(2, 2)上的棋子(图中绿色圆圈所示)移动到目标位置 (3, 2)上。
要将指定块移入目标位置,必须先将空白块移入目标位置,空白块要移动到目标位置,必然是从位置(2,2)上与当前图中目标位置上的棋子交换位置,之后能与空白块交换位置的只有当前图中目标位置上的那个棋子,因此目标棋子永远无法走到它的目标位置,游戏无法完成。
【数据范围】
对于 30%的数据,1 ≤ n, m ≤ 10,q = 1;
对于 60%的数据,1 ≤ n, m ≤ 30,q ≤ 10;
对于 100%的数据,1 ≤ n, m ≤ 30,q ≤ 500。
很明确的是要想把格子S移到目标位置T,首先得把白格子E移到S的旁边,在通过一系列的操作使格子S到达T
格子S想要走到与它相邻的k方向(left,right,up,down)上的格子,必须先把E格子通过w的花费移到那里,然后在用1的花费使S走到那 里,总花费为w+1,由于从一个格子向相邻方向上走一步的花费要多次使用,因此我们把它预处理成数组move[x][y][k][h],表示从(x,y)这个棋子,白格子在k方向上,使棋子走到h方向上相邻的格子的最少花费,这个可以用BFS预处理
有了这么多东西,能不能用BFS做呢?不行。从一个点走到相邻的节点的花费不一定是1,而且还受白格子所在方向的影响,所以就不再设状态为(x,y),而是(x,y,k),k表示白格子的方向,dist[x][y][k]存储走到这个状态的最小步数。很明显,从一个格子向h方向走的最小代价就是move[x][y][k][h],很明显这个状态只能转移到(x,y,other(h)),other(h)表示和h相反的方向,比如你向右走完一次,白格子一定在这个格子的左侧。
现在已经构建了一个又状态(x,y,k)构成的一张图,边权也已经算出来了,因此想要从出发点找一条到结束点的最短路径,可以用SPFA或堆Dijkstra,我更喜欢SPFA,因为一开始你要让四个状态入队,SFPA更方便一些,事实证明还是很快的
测试点#puzzle1.in 结果: 内存使用量: 364kB 时间使用量: 1ms
测试点#puzzle10.in 结果: 内存使用量: 616kB 时间使用量: 0ms
测试点#puzzle11.in 结果: 内存使用量: 1004kB 时间使用量: 2ms
测试点#puzzle12.in 结果: 内存使用量: 1384kB 时间使用量: 7ms
测试点#puzzle13.in 结果: 内存使用量: 2024kB 时间使用量: 11ms
测试点#puzzle14.in 结果: 内存使用量: 2536kB 时间使用量: 28ms
测试点#puzzle15.in 结果: 内存使用量: 3052kB 时间使用量: 48ms
测试点#puzzle16.in 结果: 内存使用量: 3048kB 时间使用量: 52ms
测试点#puzzle17.in 结果: 内存使用量: 2028kB 时间使用量: 11ms
测试点#puzzle18.in 结果: 内存使用量: 2540kB 时间使用量: 27ms
测试点#puzzle19.in 结果: 内存使用量: 3052kB 时间使用量: 51ms
测试点#puzzle2.in 结果: 内存使用量: 492kB 时间使用量: 0ms
测试点#puzzle20.in 结果: 内存使用量: 2924kB 时间使用量: 50ms
测试点#puzzle3.in 结果: 内存使用量: 488kB 时间使用量: 1ms
测试点#puzzle4.in 结果: 内存使用量: 364kB 时间使用量: 1ms
测试点#puzzle5.in 结果: 内存使用量: 492kB 时间使用量: 0ms
测试点#puzzle6.in 结果: 内存使用量: 492kB 时间使用量: 0ms
测试点#puzzle7.in 结果: 内存使用量: 616kB 时间使用量: 1ms
测试点#puzzle8.in 结果: 内存使用量: 876kB 时间使用量: 4ms
测试点#puzzle9.in 结果: 内存使用量: 1388kB 时间使用量: 8ms
#include <cstdio>
#include <queue>
#include <cstring>
#include <iostream>
#define up 1
#define down 2
#define left 3
#define right 4
#define maxn 35
#define inf 0x3f3f3f3fusing namespace std;struct node
{int x, y, k;
}t1, t2;
int pan[maxn][maxn], dist[maxn][maxn][5], Ex, Ey, move[maxn][maxn][5][5], deep[maxn][maxn], n, m;
bool in[maxn][maxn][5], done[maxn][maxn];
queue<node> q, qb;inline int other(int k)
{if(k==up)return down;if(k==down)return up;if(k==left)return right;if(k==right)return left;
}node go(node n, int k)
{node t=n;if(k==up)t.x--;if(k==down)t.x++;if(k==left)t.y--;if(k==right)t.y++;return t;
}int bfs(const node s, const node t)
{if(pan[s.x][s.y]==0 || pan[s.x][s.y]==0)return inf;memset(deep,0x3f,sizeof(deep));memset(done,0,sizeof(done));qb=*(new queue<node>);int i, j, k, h;node now, tmp;qb.push(s);deep[s.x][s.y]=0;done[s.x][s.y]=true;while(!qb.empty() && !done[t.x][t.y]){now=qb.front();qb.pop();for(k=1;k<=4;k++){tmp=go(now,k);if(!done[tmp.x][tmp.y] && pan[tmp.x][tmp.y]==1){done[tmp.x][tmp.y]=true;qb.push(tmp);deep[tmp.x][tmp.y] = deep[now.x][now.y]+1;}}}return deep[t.x][t.y];
}
void init() //初始化move数组
{memset(move,0x3f,sizeof(move));int i, j, k, h;for(i=1;i<=n;i++)for(j=1;j<=m;j++){if(pan[i][j]==0)continue;pan[i][j]=0; for(k=1;k<=4;k++){for(h=1;h<=4;h++){if(h<k){move[i][j][k][h]=move[i][j][h][k];continue;}t1=go((node){i,j},k);t2=go((node){i,j},h);if(pan[t1.x][t1.y]==0 || pan[t2.x][t2.y]==0)continue;move[i][j][k][h]=bfs(t1,t2)+1;}}pan[i][j]=1;}
}int spfa(const node S, const node T)
{if(S.x==T.x && S.y==T.y)return 0;node now, t;int i, j, k, h, ans;memset(dist,0x3f,sizeof(dist));memset(in,0,sizeof(in));if(pan[S.x][S.y]==0 || pan[T.x][T.y]==0)return inf;pan[S.x][S.y]=0;q=*(new queue<node>);for(k=1;k<=4;k++){t=(node){S.x,S.y,k};q.push(t), in[S.x][S.y][k]=true;dist[S.x][S.y][k]=bfs((node){Ex,Ey},go(S,k));}pan[S.x][S.y]=1;while(!q.empty()){now=q.front();q.pop();in[now.x][now.y][now.k]=false;for(h=1;h<=4;h++){t=go(now,h);t.k=other(h);if(dist[now.x][now.y][now.k] + move[now.x][now.y][now.k][h] < dist[t.x][t.y][other(h)]){dist[t.x][t.y][other(h)]=dist[now.x][now.y][now.k]+move[now.x][now.y][now.k][h];if(!in[t.x][t.y][t.k])q.push(t), in[t.x][t.y][t.k]=true;}}}ans=inf;for(k=1;k<=4;k++)ans = min(ans,dist[T.x][T.y][k]);return ans;
}
int main()
{int q, sx, sy, tx, ty, i, j, k, ans;scanf("%d%d%d",&n,&m,&q);for(i=1;i<=n;i++)for(j=1;j<=m;j++)scanf("%d",&pan[i][j]);init();for(i=1;i<=q;i++){scanf("%d%d%d%d%d%d",&Ex,&Ey,&sx,&sy,&tx,&ty);ans=spfa((node){sx,sy},(node){tx,ty});if(ans<inf)printf("%d\n",ans);else printf("-1\n");}return 0;
}