NOIP2013华容道

NOIP2014华容道

说起来这道题还挺有难度的,我用了两个小时才把它AC,要是在赛场上的话。。。。这种题就果断放弃了

下面步入正题

题目描述 Description

小 B 最近迷上了华容道,可是他总是要花很长的时间才能完成一次。于是,他想到用编程来完成华容道:给定一种局面,华容道是否根本就无法完成,如果能完成,最少需要多少时间。
小 B 玩的华容道与经典的华容道游戏略有不同,游戏规则是这样的:

  1. 在一个 n*m 棋盘上有 n*m 个格子,其中有且只有一个格子是空白的,其余 n*m-1个格子上每个格子上有一个棋子,每个棋子的大小都是 1*1 的;
  2. 有些棋子是固定的,有些棋子则是可以移动的;
  3. 任何与空白的格子相邻(有公共的边)的格子上的棋子都可以移动到空白格子上。 游戏的目的是把某个指定位置可以活动的棋子移动到目标位置。

给定一个棋盘,游戏可以玩 q 次,当然,每次棋盘上固定的格子是不会变的,但是棋盘上空白的格子的初始位置、指定的可移动的棋子的初始位置和目标位置却可能不同。第 i 次玩的时候,空白的格子在第 EX_i 行第 EY_i 列,指定的可移动棋子的初始位置为第 SX_i 行第 SY_i 列,目标位置为第 TX_i 行第 TY_i 列。
假设小 B 每秒钟能进行一次移动棋子的操作,而其他操作的时间都可以忽略不计。请你告诉小 B 每一次游戏所需要的最少时间,或者告诉他不可能完成游戏。

输入描述 Input Description

第一行有 3 个整数,每两个整数之间用一个空格隔开,依次表示 n、m 和 q;
接下来的 n 行描述一个 n*m 的棋盘,每行有 m 个整数,每两个整数之间用一个空格隔开,每个整数描述棋盘上一个格子的状态,0 表示该格子上的棋子是固定的,1 表示该格子上的棋子可以移动或者该格子是空白的。
接下来的 q 行,每行包含 6 个整数依次是 EX_i、EY_i、SX_i、SY_i、TX_i、TY_i,每两个整数之间用一个空格隔开,表示每次游戏空白格子的位置,指定棋子的初始位置和目标位置。

输出描述 Output Description

输出有 q 行,每行包含 1 个整数,表示每次游戏所需要的最少时间,如果某次游戏无法完成目标则输出-1。

样例输入 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


-1

数据范围及提示 Data Size & Hint

【样例说明】
棋盘上划叉的格子是固定的,红色格子是目标位置,圆圈表示棋子,其中绿色圆圈表示目标棋子。
第一次游戏,空白格子的初始位置是 (3, 2)(图中空白所示),游戏的目标是将初始位置在(1, 2)上的棋子(图中绿色圆圈所代表的棋子)移动到目标位置(2, 2)(图中红色的格子)上。
移动过程如下:

NOIP2013华容道_第1张图片

第二次游戏,空白格子的初始位置是(1, 2)(图中空白所示),游戏的目标是将初始位置在(2, 2)上的棋子(图中绿色圆圈所示)移动到目标位置 (3, 2)上。

NOIP2013华容道_第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  结果:AC    内存使用量:  364kB     时间使用量:  1ms     
测试点#puzzle10.in 结果:AC 内存使用量: 616kB 时间使用量: 0ms
测试点#puzzle11.in 结果:AC 内存使用量: 1004kB 时间使用量: 2ms
测试点#puzzle12.in 结果:AC 内存使用量: 1384kB 时间使用量: 7ms
测试点#puzzle13.in 结果:AC 内存使用量: 2024kB 时间使用量: 11ms
测试点#puzzle14.in 结果:AC 内存使用量: 2536kB 时间使用量: 28ms
测试点#puzzle15.in 结果:AC 内存使用量: 3052kB 时间使用量: 48ms
测试点#puzzle16.in 结果:AC 内存使用量: 3048kB 时间使用量: 52ms
测试点#puzzle17.in 结果:AC 内存使用量: 2028kB 时间使用量: 11ms
测试点#puzzle18.in 结果:AC 内存使用量: 2540kB 时间使用量: 27ms
测试点#puzzle19.in 结果:AC 内存使用量: 3052kB 时间使用量: 51ms
测试点#puzzle2.in 结果:AC 内存使用量: 492kB 时间使用量: 0ms
测试点#puzzle20.in 结果:AC 内存使用量: 2924kB 时间使用量: 50ms
测试点#puzzle3.in 结果:AC 内存使用量: 488kB 时间使用量: 1ms
测试点#puzzle4.in 结果:AC 内存使用量: 364kB 时间使用量: 1ms
测试点#puzzle5.in 结果:AC 内存使用量: 492kB 时间使用量: 0ms
测试点#puzzle6.in 结果:AC 内存使用量: 492kB 时间使用量: 0ms
测试点#puzzle7.in 结果:AC 内存使用量: 616kB 时间使用量: 1ms
测试点#puzzle8.in 结果:AC 内存使用量: 876kB 时间使用量: 4ms
测试点#puzzle9.in 结果:AC 内存使用量: 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 0x3f3f3f3f

using 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;
}


你可能感兴趣的:(NOIP2013华容道)