杭电1372————搜索之初讲广度优先搜索

Knight Moves

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 6571    Accepted Submission(s): 3961


Problem Description
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. 
 

Input
The 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. 
 

Output
For 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的棋盘(国际象棋)告诉你骑士(就是相当于象棋中的马,走”日“字)的初始位置,还有目标位置,问最少多少步可以到达。
这是一个非常基础的广度优先搜索的问题,刚好可以用来讲一下广搜。
以这个题目为例:
第一层有一个”圆“,表示起始坐标,看成是一个结点 第二层是指通过第一个结点所能到达的所有结点(即第一次走棋子的时候不同的走法到达的不同坐标) 第三层指从第二层的每一个坐标,(通过不同的走法)所能达到的所有的结点..... 第四层 依次类推.....直到我们找到目标结点(也就是题目中给出的第二个坐标)就停止这个过程
其中,第二层中的红色结点是第一层中结点试探性走的第一步,那么在走第二步的时候,这个结点就是起始结点。即先走的结点首先扩充结点(扩充结点的意思就是先走下一步)。结合到队列这种数据结构(具有先进先出的特点),我们就可以实现这种思路了。
值得一提的是,广度优先搜索所得出的结果,一定是步数最少的。
具体到代码实现的时候,我们还有一个问题,骑士能走的方向有很多(我们提前用常量数组把走的方式存储下来),但是我们绝对不会希望它走回头路,因此我们还应该判断当前想要试探性走的一步到达的位置,是否是曾经到达过的位置。可以通过将棋盘的坐标chess[x][y]的值置为1(或者其他的数都可以)来标记(x,y)已经走过.
/*杭电亲测已过,15MS*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#define maxn 10
using namespace std;

struct coordinate{
	int x,y,step;
};
char col_1,col_2;/*colum*/
int  sx,sy,ex,ey;/*start_x and end_x*/
int  chess[maxn][maxn];/*棋盘,同时标记是否走过*/
int dx[8] = {1,1,-1,-1,2,2,-2,-2};
int dy[8] = {2,-2,2,-2,1,-1,1,-1};/*directions*/

queue<coordinate> Q;
int inborder(int x,int y)
{
	int flag = 1;	
	if(x < 1 || x > 8 || y < 1 || y > 8 )
		flag = 0;
	return flag;
}
void BFS()
{
	struct coordinate now,next;
	
	while(!Q.empty())
		Q.pop();
	/*每次搜索前清空队列*/
	now.x = sx;
	now.y = sy;
	now.step = 0;
	Q.push(now);
	while(!Q.empty())
	{
		now = Q.front();/*取队首的元素*/ 
		Q.pop();/*队首出队*/ 
		for(int k = 0 ; k < 8 ; k ++)
		{
			next.x = now.x + dx[k];
			next.y = now.y + dy[k];/*向所有可能走的方向都走一遍*/ 
			if(inborder(next.x,next.y) && !chess[next.x][next.y])/*如果在棋盘内并且没走过*/ 
			{
				chess[next.x][next.y] = 1;/*标记为已经走过*/ 
				next.step = now.step + 1;/*步数在之前步数的基础上+1*/ 
				Q.push(next);
				if(next.x == ex && next.y == ey)/*搜索结束的判断条件*/ 
				{
					printf("To get from %c%d to %c%d takes %d knight moves.\n",col_1,sx,col_2,ex,next.step);
					return ;
				}
			}
		}
	} 
}
int main()
{

	
    while(cin>>col_1>>sx>>col_2>>ex)
	{
		if(col_1 == col_2 && sx  == ex)
			printf("To get from %c%d to %c%d takes %d knight moves.\n",col_1,sx,col_2,ex,0);
		else
		{
			memset(chess,0,sizeof(chess));
			sy = col_1 - 'a' + 1;
			ey = col_2 - 'a' + 1;
			BFS();
		}
	}
	return 0;
	
}
PS:搜索不仅仅是只用于这种走迷宫似的问题。实际上,只要一个问题可以划分成若干种状态,并且存在状态的转移,都可以用搜索来解决。
图片中的结点,不能单纯的看成是一种坐标,其实,它应该是代表着某种状态。从一种状态转换到另一种状态。


posted @ 2014-08-01 09:31  SixDayCoder  阅读(295)  评论(0编辑  收藏  举报