hdu1813 Escape from Tetris (IDA*)



Problem Description
由于整日整夜地对着这个棋盘,Lele终于走火入魔。每天一睡觉,他就会梦到自己会被人被扔进一个棋盘中,一直找不到出路,然后从梦中惊醒。久而久之,Lele被搞得精神衰弱。梦境是否会成为现实,谁也说不准,不过不怕一万只怕万一。现在Lele每次看到一个棋盘,都会想象一下自己被关进去以后要如何逃生。

Lele碰到的棋盘都是正方形的,其中有些格子是坏的,不可以走,剩下的都是可以走的。只要一走到棋盘的边沿(最外面的一圈),就算已经逃脱了。Lele梦见自己一定会被扔在一个可以走的格子里,但是不确定具体是哪一个,所以他要做好被扔在任意一个格子的准备。

现在Lele请你帮忙,对于任意一个棋盘,找出一个最短的序列,序列里可以包括"north"(地图里向上),"east"(地图里向右),"south"(地图里向下),"west"(地图里向左),这四个方向命令。不论Lele被扔在棋盘里的哪个好的格子里,都能按这个序列行走逃出棋盘。
逃脱的具体方法是:不论Lele被扔在哪里,Lele按照序列里的方向命令一个一个地走,每个命令走一格,如果走的时候会碰到坏的格子,则忽略这条命令。当然,如果已经逃脱了,就可以不考虑序列中剩下的命令了。
 

Input
本题目包含多组测试,请处理至文件结束。
每组测试第一行包含一个正整数 N (0<N<9),代表棋盘的大小是 N*N
接下来有N行,每行N个字符代表这个棋盘。
其中0代表该位置是好的,可以走,1代表该位置是坏的,不可以走。

题目数据保证,对于任意一个棋盘,都存在题目中所要求的序列
 

Output
对于每组数据,输出题目所要求的序列,序列中每个元素一行。
如果存在两个符合要求的序列,请输出字典序最小的那个序列。

两个测试之间请用一个空行隔开。
 

Sample Input
   
   
   
   
4 1101 0001 1100 1001
 

Sample Output
   
   
   
   
east north


这个题目真是坑。。每个细节都要注意好,不然就会容易TLE和WA。。首先BFS坑定没法搜的,就只能ID了。h函数要设计好,我一开始偷懒就设置的所有点到与它最近的边缘的直线距离的最大值。。然后T得我说不出话。。修改了一下,就是每个点到与它最近边缘的准确最小距离的最大值,这就需要BFS预处理,从边缘上每个点出发到棋盘内部每个点的距离一开始就算出来。然后还是TLE!!!!我想了半天,发现有个细节,就是边缘上是1的点的话就不需要从他开始BFS。。然后我就实在想不出撒子还可以把h函数弄得更大的办法了。就交了一发。。然后就WA了。。还好我马上反应过来我limit是从1开始的,试了个所有0都在边界的数据,果然给我输出了个east。。然后改了这里就过了。

还有一点,很多ID的题里面一个剪枝就是记录上一步的方向,这一步肯定不能走上一步的反方向。但是这道题不一样,即使走上一步的反方向并不一定会回到上一步完全一样的局面。

还有,网上大多数都是在dfs内部传数组表示状态,我觉得搜索的时候尽量不要传数组那样大的整个状态,因为这样如果是爆内存了它很可能提示的是TLE而不是RE。。很confusing。。宁肯花点时间复原。

程序最下面有一组我自己想比较卡的数据,能秒出那就差不多了。


#include<cstdio>
#include<cstring>
#include<queue>
#include<utility>
using namespace std;
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
#define pii pair<int,int> 
#define MP make_pair
int N, limit, ok;
const int dd[4][2] = {{0,1},{-1,0},{1,0},{0,-1}};
const char dname[4][7] = {"east","north","south","west"};//字典序
char a[15][15];
int x[100], y[100], cnt;//棋盘中所有人的下标
int res[100];
int dis[15][15];

int geth() {
	int res = 0;
	for (int i = 1; i<=cnt; ++i)
		res = Max(res, dis[x[i]][y[i]]);
	return res;
}

void dfs(int i)//这个i实际表示待确定的步骤。
{
	int h = geth();
	if (!h) { ok = 1; return; }
	if (i+h-1 > limit || i>limit || ok) return;
	bool mdf[85], flag = 0; //mdf:是否修改过,需要复原。flag:如果当前移动根本无效,直接跳过
	for (int t = 0; t<4 && !ok; ++t) {
		memset(mdf, 0, sizeof mdf);
		for (int j = 1; j<=cnt; ++j) {
			if (x[j]==1||x[j]==N||y[j]==1||y[j]==N) continue;
			if (a[x[j]+dd[t][0]][y[j]+dd[t][1]]=='1') continue;
			x[j] += dd[t][0];
			y[j] += dd[t][1]; flag = mdf[j] = 1;
		}
		res[i] = t;
		if (flag) {
			dfs(i+1);
			for (int j = 1; j<=cnt; ++j)
				if (mdf[j])
					x[j] -= dd[t][0], y[j] -= dd[t][1];
		}
	}
}

void BFS(int x, int y)
{
	queue<pii > Q;
	Q.push(MP(x,y));
	dis[x][y] = 0;
	pii s;
	int tx, ty;
	while (!Q.empty()) {
		s = Q.front(); Q.pop();
		for (int i = 0; i<4; ++i) {
			tx = s.first + dd[i][0];
			ty = s.second + dd[i][1];
			if (tx<1 || ty<1 || tx>N || ty>N) continue;
			if (a[tx][ty]=='1') continue;
			if (dis[tx][ty] <= dis[s.first][s.second]+1) continue;
			dis[tx][ty] = dis[s.first][s.second] + 1;
			Q.push(MP(tx, ty));
		}
	}
}

int main()
{
	int i, j, flag = 0;
	while (~scanf("%d", &N))
	{
		for (i = 1; i<=N; ++i)
			scanf("%s", a[i]+1);
		cnt = 0;
		for (i = 2; i<N; ++i)
			for (j = 2; j<N; ++j) {
				if (a[i][j]=='0') {
					++cnt;
					x[cnt] = i;
					y[cnt] = j;
				}
			}
		for (i = 1; i<=N; ++i)
			for (j = 1; j<=N; ++j)
				dis[i][j] = 99999;
		for (i = 1; i<=N; ++i) {
			if (a[i][1] == '0') BFS(i, 1);
			if (a[i][N] == '0') BFS(i, N);
			if (a[1][i] == '0') BFS(1, i);
			if (a[N][i] == '0') BFS(N, i);
		}
		ok = 0;
		limit = 0;//一定从零开始
		while (1) {
			dfs(1);
			if (ok) break;
			++limit;
		}
		if (flag++) puts("");
		for (i = 1; i<=limit; ++i)
			puts(dname[res[i]]);
	}
	return 0;
}

/*
8
10111111
10001001
11100011
10001111
11100001
10111011
10000001
11111111

answer:seeeenwnnwswnnwnwwn
*/



Problem Description
由于整日整夜地对着这个棋盘,Lele终于走火入魔。每天一睡觉,他就会梦到自己会被人被扔进一个棋盘中,一直找不到出路,然后从梦中惊醒。久而久之,Lele被搞得精神衰弱。梦境是否会成为现实,谁也说不准,不过不怕一万只怕万一。现在Lele每次看到一个棋盘,都会想象一下自己被关进去以后要如何逃生。

Lele碰到的棋盘都是正方形的,其中有些格子是坏的,不可以走,剩下的都是可以走的。只要一走到棋盘的边沿(最外面的一圈),就算已经逃脱了。Lele梦见自己一定会被扔在一个可以走的格子里,但是不确定具体是哪一个,所以他要做好被扔在任意一个格子的准备。

现在Lele请你帮忙,对于任意一个棋盘,找出一个最短的序列,序列里可以包括"north"(地图里向上),"east"(地图里向右),"south"(地图里向下),"west"(地图里向左),这四个方向命令。不论Lele被扔在棋盘里的哪个好的格子里,都能按这个序列行走逃出棋盘。
逃脱的具体方法是:不论Lele被扔在哪里,Lele按照序列里的方向命令一个一个地走,每个命令走一格,如果走的时候会碰到坏的格子,则忽略这条命令。当然,如果已经逃脱了,就可以不考虑序列中剩下的命令了。
 

Input
本题目包含多组测试,请处理至文件结束。
每组测试第一行包含一个正整数 N (0<N<9),代表棋盘的大小是 N*N
接下来有N行,每行N个字符代表这个棋盘。
其中0代表该位置是好的,可以走,1代表该位置是坏的,不可以走。

题目数据保证,对于任意一个棋盘,都存在题目中所要求的序列
 

Output
对于每组数据,输出题目所要求的序列,序列中每个元素一行。
如果存在两个符合要求的序列,请输出字典序最小的那个序列。

两个测试之间请用一个空行隔开。
 

Sample Input
    
    
    
    
4 1101 0001 1100 1001
 

Sample Output
    
    
    
    
east north

你可能感兴趣的:(hdu1813 Escape from Tetris (IDA*))