2018SCUACM Training 2 DFS/BFS

6 篇文章 0 订阅
1 篇文章 0 订阅

emmmmm单纯因为懒的关系。。。所以week 04都end了才写这篇。。。觉得还是写一写这个东西对自己也有帮助吧。。。虽然别人不一定看得到,看得到的也不一定有用就是了。。。

2018SCUACM Training 2 DFS/BFS


好了。。步入正题


在N*N的方格棋盘放置了N个皇后,使得它们不相互攻击(即任意2个皇后不允许处在同一排,同一列,也不允许处在与棋盘边框成45角的斜线上。 
你的任务是,对于给定的N,求出有多少种合法的放置方法。 

Input共有若干行,每行一个正整数N≤10,表示棋盘和皇后的数量;如果N=0,表示结束。Output共有若干行,每行一个正整数,表示对应输入行的皇后的不同放置数量。Sample Input
1
8
5
0
Sample Output
1
92
10

这是典型的DFS题,DFS有一个特点就是层层深入,直到终点或是不能更深入为止,然后再返回上一个状态继续寻找。所以一般的dfs代码上下都会有明显的对称性。

对于这个问题,我们可以开一个数组a,a[i]记录的是第i行的皇后放所在的列数,由于n个皇后每个皇后都不同行,所以a[i]一定是存在的,由于n个皇后每个皇后都不同列,所以a[i]两两互异。

判断互异最简单的办法就是遍历一遍之前的a[i],看有没有出现过这一列,但也可以采用空间换时间的办法,即开一个book数组,book[i]记录的是第i列是否已经被使用过。

最后还要求皇后不在同一个斜线上,判断这个条件可以联想坐标系,和x轴或y轴成45°夹角的直线的斜率是1或-1,也就是可以判断两个点的横坐标之差和纵坐标之差是不是满足绝对值相等。所以放在这里,也就是i和j之差的绝对值是否等于a[i]和a[j]之差的绝对值

写递归函数一定要记得写临界条件!!!

#include<stdio.h>
#include<string.h>
#include<math.h>
int ans,n;
int book[15];//book[i]表示第i行放的皇后的列数 、
int a[15];//a[i]记录是否使用过数字i 
void dfs(int t)
{
	if(t > n)//已经放到第n+1行了 
	{
		ans++;//前n行有了一种方法 
		return;
	}
	int i,j,flag;
	for(i = 1; i <= n; i++)//准备在第t行第i列放一个皇后 
	{
		if(a[i] == 1)
			continue;
		flag = 1;//用于判断是否能与前面几行不冲突 
		for(j = 1; j < t; j++)//遍历前面的t行  
			if(abs(j-t) == abs(i-book[j]))
			{
				flag = 0;
				break;
			}
		if(flag == 1)
		{
			a[i] = 1;
			book[t] = i;
			dfs(t+1);
			book[t] = 0;
			a[i] = 0;
		}
	}
}
int main()
{
	while(scanf("%d",&n) && n)
	{
		ans = 0;
		memset(book,0,sizeof(book));
		memset(a,0,sizeof(a));
		dfs(1);
		printf("%d\n",ans);
	}
	return 0;
}


给出一个字符串S(可能有重复的字符),按照字典序从小到大,输出S包括的字符组成的所有排列。例如:S = "1312",
输出为:

1123
1132
1213
1231
1312
1321
2113
2131
2311
3112
3121
3211
Input输入一个字符串S(S的长度 <= 9,且只包括0 - 9的阿拉伯数字)Output输出S所包含的字符组成的所有排列Sample Input
1312
Sample Output
1123
1132
1213
1231
1312
1321
2113
2131
2311
3112
3121
3211

输出全排列,实际上也是一个典型的DFS题,全排列的顺序其实刚好就是DFS的搜索顺序,每次都按照一个特定的次序搜索,在这里体现为每次都在当前位放一个尽可能小的数字,比如第一个放进来的数就是1123,前面112已经放完之后第四位没有其他选择,所以回退到放第三位的情况,第三位的2已经DFS完毕,则在这里放3,第四位就只能放2,所以第二个数是1132

所以每次DFS做的事情就是,找到当前剩余数字中的最小的,然后记录用掉一个这个数,然后进行下一层DFS,下一层DFS结束后,回复先前的状态,即把这个数加回来,找到第二个数并记录使用,再进行下一次DFS,直到枚举完这一层可能出现的所有数。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
char s[15];
int cnt[10];
int l;
void dfs(int n)
{
	if(n == l)
	{
		puts(s);
		return;
	}
	int i;
	for(i = 0; i < 10; i++)
	{
		if(cnt[i] != 0)
		{
			cnt[i]--;
			s[n] = i+'0';
			dfs(n+1);
			cnt[i]++;
		}
	}
}
int main()
{
	while(scanf("%s",s) != EOF)
	{
		memset(cnt,0,sizeof(cnt));
		int i;
		l = strlen(s);
		for(i = 0; i < l; i++)
			cnt[s[i]-'0']++;
		dfs(0);
	}
	return 0;
}

C - Oil Deposits

  HDU - 1241

The GeoSurvComp geologic survey company is responsible for detecting underground oil deposits. GeoSurvComp works with one large rectangular region of land at a time, and creates a grid that divides the land into numerous square plots. It then analyzes each plot separately, using sensing equipment to determine whether or not the plot contains oil. A plot containing oil is called a pocket. If two pockets are adjacent, then they are part of the same oil deposit. Oil deposits can be quite large and may contain numerous pockets. Your job is to determine how many different oil deposits are contained in a grid. 
InputThe input file contains one or more grids. Each grid begins with a line containing m and n, the number of rows and columns in the grid, separated by a single space. If m = 0 it signals the end of the input; otherwise 1 <= m <= 100 and 1 <= n <= 100. Following this are m lines of n characters each (not counting the end-of-line characters). Each character corresponds to one plot, and is either `*', representing the absence of oil, or `@', representing an oil pocket. 
OutputFor each grid, output the number of distinct oil deposits. Two different pockets are part of the same oil deposit if they are adjacent horizontally, vertically, or diagonally. An oil deposit will not contain more than 100 pockets. 
Sample Input
1 1
*
3 5
*@*@*
**@**
*@*@*
1 8
@@****@*
5 5 
****@
*@@*@
*@**@
@@@*@
@@**@
0 0 
Sample Output
0
1
2
2

题目大意是:给一张地图,地图中相邻8个方向的点认为是连通的,互相连通的若干个点之间可以看成一块,问地图中有多少块'@'

这也算是一个DFS吧(好像BFS也能做,其实很多DFS的图都能用BFS做,只是我这里是用DFS做的)。看过搜索的人应该知道这个经典的问题。

对地图中的每个点都进行一次DFS(有先后顺序,也就是前面的点的DFS可能会影响到后面结点的DFS条件),每次DFS的时候,如果这个点是'*'或者是已经访问过的'@',则不进行任何操作。如果这个点是'@',就记录块数+1,这时候因为我们已经对这块进行了计数,所以我们要把这块标记为已经访问过(如果不需要重复使用地图中的信息,就可以把这块'@'“标记”为'*',也就是已经计数了,如果需要再次引用,那也可以采取其他的标记方案)。标记这块已经访问过,就是DFS的工作,每次DFS枚举访问8个方向,如果是'*'或已经标记过的'@',不做任何操作,直接退出,如果是没被标记过的'@',就进行标记,并继续枚举4个方向。

#include<stdio.h>
char map[105][105];
int m,n;
void dfs(int x, int y)
{
	map[x][y] = '*';
	int nx[] = {0,0,1,1,1,-1,-1,-1};
	int ny[] = {1,-1,1,-1,0,1,-1,0};
	int k;
	for(k = 0; k < 8; k++)
		if(0 <= x+nx[k] && x+nx[k] < m
		&& 0 <= y+ny[k] && y+ny[k] < n
		&& map[x+nx[k]][y+ny[k]] == '@')
			dfs(x+nx[k], y+ny[k]);
}
int main()
{
	int i,j,cnt;
	while(scanf("%d%d",&m,&n) && m && n)
	{
		gets(map[0]);
		cnt = 0;
		for(i = 0; i < m; i++)
			gets(map[i]);
		for(i = 0; i < m; i++)
		for(j = 0; j < n; j++)
			if(map[i][j] == '@')
			{
				cnt++;
				dfs(i,j);
			}
		printf("%d\n",cnt);
	}
	return 0;
}

A friend of you is doing research on the Traveling Knight Problem (TKP) where you are to find the shortest closed tour of knight moves that visits each square of a given set of n squares on a chessboard exactly once. He thinks that the most difficult part of the problem is determining the smallest number of knight moves between two given squares and that, once you have accomplished this, finding the tour would be easy. 
Of course you know that it is vice versa. So you offer him to write a program that solves the "difficult" part. 

Your job is to write a program that takes two squares a and b as input and then determines the number of knight moves on a shortest route from a to b. 
InputThe input file will contain one or more test cases. Each test case consists of one line containing two squares separated by one space. A square is a string consisting of a letter (a-h) representing the column and a digit (1-8) representing the row on the chessboard. 
OutputFor each test case, print one line saying "To get from xx to yy takes n knight moves.". 
Sample Input
e2 e4
a1 b2
b2 c3
a1 h8
a1 h7
h8 a1
b1 c3
f6 f6
Sample Output
To get from e2 to e4 takes 2 knight moves.
To get from a1 to b2 takes 4 knight moves.
To get from b2 to c3 takes 2 knight moves.
To get from a1 to h8 takes 6 knight moves.
To get from a1 to h7 takes 5 knight moves.
To get from h8 to a1 takes 6 knight moves.
To get from b1 to c3 takes 1 knight moves.
To get from f6 to f6 takes 0 knight moves.


题目大意:国际象棋的棋盘是8*8的矩阵,问其中马从某个位置跳到另一个位置需要的最少步数。国际象棋的马和中国象棋的马走法类似,都是走“日”字形,不同的是国际象棋没有“绊马腿”的说法。

这是一个很显然的BFS题。在搜索专题中有一句经典的话叫做“深搜配栈,广搜配队”,所以写这个题之前要先了解队列这个数据结构(为什么写DFS不用了解栈呢?因为到现在为止的大多数情况下,我们写的是递归的DFS,而函数的递归在系统中本身就存在一个栈中)。

队列是一个满足“先进先出”的数据结构,我们可以往队列中插入元素,也可以从队列中提取出元素,但是插入元素只能往队尾插入,提出元素只能从队首提出,就类似排队打饭一样,排队只能排到最后,打完饭离队只能从最前面离队。

那么怎么用BFS呢?我们的基本思路是:枚举出所有一步能够到达的点,再枚举出所有2步能到达的点,直到枚举过程中出现了终点,那么输出结果。

而在已知i步能够到达的所有点的情况下,要枚举出i+1步能够到达的点,即对第i步能到达的所有点进行一次判断,判断的内容是这个点可以到达的所有顶点是否可以更新。可以更新是指,这个点在1-i步都是不能到达的,而第i+1步可以到达(因为你之前已经到达了就不需要存一个更长步数的方法了)

怎么枚举所有第i步能到达的点呢?我们首先把起点加入队列,然后当队列不为空时进行循环:将队首元素出队,并将对首元素可能到达的所有点中可以更新的点添加到队尾。这样当i步能到达的所有顶点出队后,队列中存的就是i+1步才能到达的所有顶点了。

#include<stdio.h>
#include<string.h>
int main()
{
	int cnt[10][10];
	int q1[70], q2[70];
	int nx[] = {1,1,2,2,-1,-1,-2,-2};
	int ny[] = {2,-2,1,-1,2,-2,1,-1};
	int head,tail,p,q,i;
	char s1[5],s2[5];
	while(scanf("%s",s1) != EOF)
	{
		memset(cnt,-1,sizeof(cnt));
		p = s1[0]-'a';//起点 
		q = s1[1]-'0'-1;
		q1[0] = p;
		q2[0] = q;
		head = 0;
		tail = 1;
		cnt[p][q] = 0;
		scanf("%s",s2);
		p = s2[0]-'a';//终点 
		q = s2[1]-'0'-1;
		while(head < tail)
		{
			for(i = 0; i < 8; i++)
			{
				if(0 <= q1[head]+nx[i] && q1[head]+nx[i] < 8
				&& 0 <= q2[head]+ny[i] && q2[head]+ny[i] < 8
				&& cnt[q1[head]+nx[i]][q2[head]+ny[i]] == -1)
				{
					cnt[q1[head]+nx[i]][q2[head]+ny[i]] = cnt[q1[head]][q2[head]]+1;
					if(q1[head]+nx[i] == p && q2[head]+ny[i] == q)
					{
						head = tail;
						break;
					}
					q1[tail] = q1[head]+nx[i];
					q2[tail] = q2[head]+ny[i];
					tail++;
				}
			}
			head++;
		}
		printf("To get from %s to %s takes %d knight moves.\n",s1,s2,cnt[p][q]);
	}
	return 0;
}

自从2006年3月10日至11日的首届数独世界锦标赛以后,数独这项游戏越来越受到人们的喜爱和重视。 
据说,在2008北京奥运会上,会将数独列为一个单独的项目进行比赛,冠军将有可能获得的一份巨大的奖品———HDU免费七日游外加lcy亲笔签名以及同hdu acm team合影留念的机会。 
所以全球人民前仆后继,为了奖品日夜训练茶饭不思。当然也包括初学者linle,不过他太笨了又没有多少耐性,只能做做最最基本的数独题,不过他还是想得到那些奖品,你能帮帮他吗?你只要把答案告诉他就可以,不用教他是怎么做的。 

数独游戏的规则是这样的:在一个9x9的方格中,你需要把数字1-9填写到空格当中,并且使方格的每一行和每一列中都包含1-9这九个数字。同时还要保证,空格中用粗线划分成9个3x3的方格也同时包含1-9这九个数字。比如有这样一个题,大家可以仔细观察一下,在这里面每行、每列,以及每个3x3的方格都包含1-9这九个数字。 

例题: 
 

答案: 
 
Input本题包含多组测试,每组之间由一个空行隔开。每组测试会给你一个 9*9 的矩阵,同一行相邻的两个元素用一个空格分开。其中1-9代表该位置的已经填好的数,问号(?)表示需要你填的数。 
Output对于每组测试,请输出它的解,同一行相邻的两个数用一个空格分开。两组解之间要一个空行。 
对于每组测试数据保证它有且只有一个解。 
Sample Input
7 1 2 ? 6 ? 3 5 8
? 6 5 2 ? 7 1 ? 4
? ? 8 5 1 3 6 7 2
9 2 4 ? 5 6 ? 3 7
5 ? 6 ? ? ? 2 4 1
1 ? 3 7 2 ? 9 ? 5
? ? 1 9 7 5 4 8 6
6 ? 7 8 3 ? 5 1 9
8 5 9 ? 4 ? ? 2 3
Sample Output
7 1 2 4 6 9 3 5 8
3 6 5 2 8 7 1 9 4
4 9 8 5 1 3 6 7 2
9 2 4 1 5 6 8 3 7
5 7 6 3 9 8 2 4 1
1 8 3 7 2 4 9 6 5
2 3 1 9 7 5 4 8 6
6 4 7 8 3 2 5 1 9
8 5 9 6 4 1 7 2 3

题目相信大家都看得懂,就是常见的数独游戏。

只要尝试每个空白格子中可能填入的数的所有情况即可,明显的DFS。

#include<iostream>
using namespace std;
int map[10][10];
int flag;
int judge(int x, int y, int key)
{
	int i,j,k;
	int nx[] = {0,1,2,0,1,2,0,1,2};
	int ny[] = {0,0,0,1,1,1,2,2,2};
	for(i = 1; i <= 9; i++)
	{
		if(map[i][y] == key && i != x)//查同一列 
			return 0;
		if(map[x][i] == key && i != y)//查同一行 
			return 0;
	}
	i = (x-1)/3*3+1; j = (y-1)/3*3+1;
	for(k = 0; k < 9; k++)//查同一个九宫格 
		if(map[i+nx[k]][j+ny[k]] == key && i+nx[k] != x && j+ny[k] != y)
			return 0;
	return 1;
}
void print()
{
	int i,j;
	for(i = 1; i <= 9; i++)
	{
		for(j = 1; j < 9; j++)
			printf("%d ",map[i][j]);
		printf("%d",map[i][j]);
		printf("\n");
	}
}
void dfs(int n)
{
	if(n > 81)
	{
		print();
		flag = 0;
		return;
	}
	int k;
	for(k = 1; k <= 9; k++)
	{
		if(judge((n-1)/9+1,(n+8)%9+1,k))
		{
			map[(n-1)/9+1][(n+8)%9+1] = k;
			int t = n+1;
			while(map[(t-1)/9+1][(t+8)%9+1] != 0 && t <= 81)
				t++;
			dfs(t);
			if(flag == 0)
				return;
			map[(n-1)/9+1][(n+8)%9+1] = 0;
		}
	}
}
int main()
{
	int i,j,mmp = 0;
	char ch;
	while(1)
	{
		for(i = 1; i <= 9; i++)
		for(j = 1; j <= 9; j++)
		{
			if(!(cin>>ch))
				return 0;
			if('1' <= ch && ch <= '9')
				map[i][j] = ch - '0';
			else if(ch == '?')
				map[i][j] = 0;
		}
		flag = i = 1;
		while(map[(i-1)/9+1][(i+8)%9+1] != 0 && i <= 81)
			i++;
		if(mmp++)
			printf("\n");
		dfs(i);
	}
	return 0;
}

F - Herbs Gathering

  HDU - 5887

Collecting one's own plants for use as herbal medicines is perhaps one of the most self-empowering things a person can do, as it implies that they have taken the time and effort to learn about the uses and virtues of the plant and how it might benefit them, how to identify it in its native habitat or how to cultivate it in a garden, and how to prepare it as medicine. It also implies that a person has chosen to take responsibility for their own health and well being, rather than entirely surrender that faculty to another. Consider several different herbs. Each of them has a certain time which needs to be gathered, to be prepared and to be processed. Meanwhile a detailed analysis presents scores as evaluations of each herbs. Our time is running out. The only goal is to maximize the sum of scores for herbs which we can get within a limited time.
InputThere are at most ten test cases. 
For each case, the first line consists two integers, the total number of different herbs and the time limit. 
The  [Math Processing Error]i-th line of the following  [Math Processing Error]n line consists two non-negative integers. The first one is the time we need to gather and prepare the  [Math Processing Error]i-th herb, and the second one is its score. 

The total number of different herbs should be no more than  [Math Processing Error]100. All of the other numbers read in are uniform random and should not be more than  [Math Processing Error]109.OutputFor each test case, output an integer as the maximum sum of scores.Sample Input
3 70
71 100
69 1
1 2
Sample Output
3


题目就不解释了,emmmm题目公式没显示出来的话请移步HDU-5887。因为这题和最普通的01背包问题一毛一样,只是换了种表达方式,换了个数据范围。

当时第一眼看到这个题目,呀!这不是背包么。

仔细一看数据范围,woc,数组开不出来。

所以就只能DFS+剪枝了

DFS枚举每个植物能不能选,复杂度O(2^n),肯定会炸

但是确实没啥更好的办法了,毕竟数组开不出来,所以只能开发个好的剪枝函数来优化了

有什么办法剪枝呢?首先,枚举的时候可以先预处理一下剩余物品的价值之和,如果还没选的物品全部选了,都还不能达到已经有了的最大值,那就不需要继续搜索下去了嘛。其次,类似的,如果剩余物品的耗费之和还没达到现在拥有的限制,就可以直接把剩下的物品全部拿掉,也不用继续搜索了。

还有一个弱一点的剪枝是可以把物品按某种规律排个序,一般是按性价比排序,也就是价值与消耗之比,虽然01背包问题是个离散的问题,但数据量够大的时候,还是可以用最基本的背包问题(物品可以分割)来“伪贪心”的

#include<iostream>
#include<cstdio>
#include<algorithm>
#define maxn 105
using namespace std;
typedef long long LL;
typedef struct
{
	LL cost,value;
} node;
node a[maxn];
LL sumv[maxn],sumc[maxn];
LL n,time,ans;
int cmp(node a, node b)
{
	return a.cost < b.cost;
}
void dfs(LL num, LL spend, LL val)
{
	if(num >= n || spend >= time)
	{
		if(val > ans)
			ans = val;
		return;
	}
	if(val + sumv[num] <= ans)
		return;
	if(spend + sumc[num] <= time)
	{
		if(val + sumv[num] > ans)
			ans = val+sumv[num];
		return;
	}
	dfs(num+1, spend, val);
	if(spend + a[num].cost <= time)
		dfs(num+1, spend+a[num].cost, val+a[num].value);
}
int main()
{
	int i;
	while(scanf("%lld%lld",&n,&time) != EOF)
	{
		ans = 0;
		for(i = 0; i < n; i++)
			scanf("%lld%lld",&a[i].cost, &a[i].value);
		sort(a,a+n,cmp);
		sumv[n-1] = a[n-1].value;
		sumc[n-1] = a[n-1].cost;
		for(i = n-2; i >= 0; i--)
		{
			sumv[i] = sumv[i+1] + a[i].value;
			sumc[i] = sumc[i+1] + a[i].cost;
		}
		dfs(0,0,0);
		printf("%lld\n",ans);
	}
	return 0;
}

G - Dungeon Master

  ZOJ - 1940

You are trapped in a 3D dungeon and need to find the quickest way out! The dungeon is composed of unit cubes which may or may not be filled with rock. It takes one minute to move one unit north, south, east, west, up or down. You cannot move diagonally and the maze is surrounded by solid rock on all sides.

Is an escape possible? If yes, how long will it take?


Input

The input consists of a number of dungeons. Each dungeon description starts with a line containing three integers L, R and C (all limited to 30 in size).

L is the number of levels making up the dungeon. 

R and C are the number of rows and columns making up the plan of each level. 

Then there will follow L blocks of R lines each containing C characters. Each character describes one cell of the dungeon. A cell full of rock is indicated by a '#' and empty cells are represented by a '.'. Your starting position is indicated by 'S' and the exit by the letter 'E'. There's a single blank line after each level. Input is terminated by three zeroes for L, R and C.


Output

Each maze generates one line of output. If it is possible to reach the exit, print a line of the form 

Escaped in x minute(s). 

where x is replaced by the shortest time it takes to escape. 

If it is not possible to escape, print the line 

Trapped!


Sample Input

3 4 5
S....
.###.
.##..
###.#

#####
#####
##.##
##...

#####
#####
#.###
####E

1 3 3
S##
#E#
###

0 0 0


Sample Output

Escaped in 11 minute(s).
Trapped!


题目大意:走迷宫问题,给你起点终点问你从起点到终点耗时最短多少。只不过这题是3维的而已。

也是属于BFS的板子题了,类似于前面国际象棋那题,先枚举一步能到达的地方,再两步,不断增加。如果队列全部清空了,说明可以到达的点已经全部搜索过了,如果还没有搜索到终点,那么就没有出口。否则在搜索到终点的时候就可以输出答案了。

#include<stdio.h>
#include<string.h>
int l,r,c;
char map[35][35][35];
int move[35][35][35];
int main()
{
	int i,j,k,head,tail,P,Q,R;
	int nx[] = {-1,1,0,0,0,0};
	int ny[] = {0,0,-1,1,0,0};
	int nz[] = {0,0,0,0,-1,1};
	int q1[30000];
	int q2[30000];
	int q3[30000];
	while(scanf("%d%d%d",&l,&r,&c) && l && r && c)
	{
		memset(move,-1,sizeof(move));
		for(i = 0; i < l; i++)
		{
			for(j = 0; j < r; j++)
				scanf("%s",map[i][j]);
		}
		head = 0;
		tail = 0;
		for(i = 0; i < l; i++)
		for(j = 0; j < r; j++)
		for(k = 0; k < c; k++)
			if(map[i][j][k] == 'S')
			{
				q1[tail] = i;
				q2[tail] = j;
				q3[tail] = k;
				tail++;
				move[i][j][k] = 0;
			}
			else if(map[i][j][k] == 'E')
			{
				P = i; Q = j; R = k;
			}
		while(head < tail)
		{
			for(i = 0; i < 6; i++)
			{
				if( 0 <= q1[head]+nx[i] && q1[head]+nx[i] < l
				&&  0 <= q2[head]+ny[i] && q2[head]+ny[i] < r
				&&  0 <= q3[head]+nz[i] && q3[head]+nz[i] < c
				&& map[q1[head]+nx[i]][q2[head]+ny[i]][q3[head]+nz[i]] != '#'
				&& move[q1[head]+nx[i]][q2[head]+ny[i]][q3[head]+nz[i]] == -1) 
				{
					move[q1[head]+nx[i]][q2[head]+ny[i]][q3[head]+nz[i]] = move[q1[head]][q2[head]][q3[head]]+1;
					if(map[q1[head]+nx[i]][q2[head]+ny[i]][q3[head]+nz[i]] == 'E')
					{
						head = tail;
						break;
					}
					q1[tail] = q1[head]+nx[i];
					q2[tail] = q2[head]+ny[i];
					q3[tail] = q3[head]+nz[i];
					tail++;
				}
			}
			head++;
		}

		if(move[P][Q][R] == -1)
			printf("Trapped!\n");
		else
			printf("Escaped in %d minute(s).\n",move[P][Q][R]);
	}
	return 0;
}

H - Eight

  HDU - 1043 

The 15-puzzle has been around for over 100 years; even if you don't know it by that name, you've seen it. It is constructed with 15 sliding tiles, each with a number from 1 to 15 on it, and all packed into a 4 by 4 frame with one tile missing. Let's call the missing tile 'x'; the object of the puzzle is to arrange the tiles so that they are ordered as: 
 1  2  3  4
 5  6  7  8
 9 10 11 12
13 14 15  x

where the only legal operation is to exchange 'x' with one of the tiles with which it shares an edge. As an example, the following sequence of moves solves a slightly scrambled puzzle: 
 1  2  3  4     1  2  3  4     1  2  3  4     1  2  3  4
 5  6  7  8     5  6  7  8     5  6  7  8     5  6  7  8
 9  x 10 12     9 10  x 12     9 10 11 12     9 10 11 12
13 14 11 15    13 14 11 15    13 14  x 15    13 14 15  x
            r->            d->            r->

The letters in the previous row indicate which neighbor of the 'x' tile is swapped with the 'x' tile at each step; legal values are 'r','l','u' and 'd', for right, left, up, and down, respectively. 

Not all puzzles can be solved; in 1870, a man named Sam Loyd was famous for distributing an unsolvable version of the puzzle, and 
frustrating many people. In fact, all you have to do to make a regular puzzle into an unsolvable one is to swap two tiles (not counting the missing 'x' tile, of course). 

In this problem, you will write a program for solving the less well-known 8-puzzle, composed of tiles on a three by three 
arrangement. 
InputYou will receive, several descriptions of configuration of the 8 puzzle. One description is just a list of the tiles in their initial positions, with the rows listed from top to bottom, and the tiles listed from left to right within a row, where the tiles are represented by numbers 1 to 8, plus 'x'. For example, this puzzle 

1 2 3 
x 4 6 
7 5 8 

is described by this list: 

1 2 3 x 4 6 7 5 8 
OutputYou will print to standard output either the word ``unsolvable'', if the puzzle has no solution, or a string consisting entirely of the letters 'r', 'l', 'u' and 'd' that describes a series of moves that produce a solution. The string should include no spaces and start at the beginning of the line. Do not print a blank line between cases. 
Sample Input
2  3  4  1  5  x  7  6  8
Sample Output
ullddrurdllurdruldr

相信大家都玩过拼图的游戏,就是那个16个正方形组成的拼图,然后拿掉最后那快,打乱前面15块,然后复原的那种。(emmmm玩过的应该都知道,没玩过的我觉得我也没讲清楚)

然后现在就是要用程序把这个问题的弱化版——3*3的拼图给解出来,即八数码问题。

至于怎么解,我不方便说,因为自己就没做出来。。

反正网上的题解不外乎A*算法,康托展开,HASH,双向BFS,等等。

所以还是建议各位去百度吧。。我这里贴一份我自己写的康托展开TLE代码。。但是答案应该是没问题的

TLE TLE TLE TLE TLE TLE TLE TLE TLE TLE TLE TLE TLE TLE TLE TLE TLE TLE TLE TLE TLE TLE TLE TLE

#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
typedef struct
{
	int a[9];
} node;
int n = 9;
node map;
int fact[] = {1,1,2,6,24,120,720,5040,40320,362880,3628800};
int book[362890];
char step[362890],s[362890];
queue<node> q;
int read()
{
	char s[30],*p;
	int l,cnt = 0;
	gets(s);
	l = strlen(s);
	if(l < 10)
		return 0;
	p = s;
	while(p < s+l && cnt < n)
	{
		while(*p == ' ' && p < s+l)
			p++;
		if(*p == 'x')
			map.a[cnt++] = 9;
		else
			map.a[cnt++] = *p-'0';
		p++;
	}
	return 1;
}
int Cantor(node s)
{
	int ans = 0;
	int cnt[] = {0,0,1,2,3,4,5,6,7,8};
	int i,j;
	for(i = 0; i < n; i++)
	{
		ans += fact[n-1-i]*cnt[s.a[i]];
		for(j = s.a[i]; j <= n; j++)
			cnt[j]--;
	}
	return ans;
}
void clear_queue(queue<node>& q)
{
	queue<node> empty;
	swap(empty,q);
}
int main()
{
	int i;
	while(read() != 0)
	{
		clear_queue(q);
		memset(book,-1,sizeof(book));
		q.push(map);
		book[Cantor(map)] = 0;
		while(!q.empty())
		{
			node t = q.front();
			q.pop();
			int x = 0;
			while(t.a[x] != 9) x++;
			if(x > 2)//up
			{
				node next;
				next = t;
				next.a[x] = next.a[x-3];
				next.a[x-3] = 9;
				int tmp = Cantor(next);
				if(book[tmp] == -1)
				{
					step[tmp] = 'u';
					book[tmp] = Cantor(t);
					q.push(next);
					if(tmp == 0)
						break;
				}
			}
			if(x < 6)//down
			{
				node next;
				next = t;
				next.a[x] = next.a[x+3];
				next.a[x+3] = 9;
				int tmp = Cantor(next);
				if(book[tmp] == -1)
				{
					step[tmp] = 'd';
					book[tmp] = Cantor(t);
					q.push(next);
					if(tmp == 0)
						break;
				}
			}
			if(x%3 != 0)//left
			{
				node next;
				next = t;
				next.a[x] = next.a[x-1];
				next.a[x-1] = 9;
				int tmp = Cantor(next);
				if(book[tmp] == -1)
				{
					step[tmp] = 'l';
					book[tmp] = Cantor(t);
					q.push(next);
					if(tmp == 0)
						break;
				}
			}
			if((x+1)%3 != 0)//right
			{
				node next;
				next = t;
				next.a[x] = next.a[x+1];
				next.a[x+1] = 9;
				int tmp = Cantor(next);
				if(book[tmp] == -1)
				{
					step[tmp] = 'r';
					book[tmp] = Cantor(t);
					q.push(next);
					if(tmp == 0)
						break;
				}
			}
		}
		int cur = 0;
		int index = 0;
		while(book[cur] != 0)
		{
			s[index++] = step[cur];
			cur = book[cur];
		}
		for(index--; index>=0; index--)
			printf("%c",s[index]);
		printf("\n");
	}
	return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值