马踏棋盘(骑士周游问题),深度优先搜索

马踏棋盘问题(又称骑士周游或骑士漫游问题是算法设计的经典问题之一)。问题描述:国际象棋的棋盘是8×8的方格棋盘,先将马放在任意指定的方格中,按照马走棋的规则将马进行移动,要求每个方格只能进入一次,最终使得马走遍棋盘的64个方格。除了边缘的位置,马每次有8种走法。
关于国际象棋马的走法
虽然递归,回溯法效率很低,但是也是一种解决方式。就是按照一条路走到底碰壁了在回来走另一条路。就如上图所示,马开始走,有8种选择,比如现在选择第一种方式,然后在1的位置又有8种选择,照着下图这样走,如果有哪一次走不通了,就往回退,直到退到初始位置,说明从该位置不能遍历整个棋盘
在这里插入图片描述
代码如下

#include <stdio.h>
#include <time.h> //包含该头文件,以便计算程序用时

//棋盘大小
#define X 8
#define Y 8
//用二维数组表示棋盘
int chess[X][Y];


//打印棋盘,棋盘是全局变量,这里就不写函数参数了
void print()
{
	for (int i = 0; i < Y; i++)
	{
		for (int j = 0; j < X; j++)
		{
			//输出长度为2,不足左补空格
			printf("%2d  ", chess[i][j]);
		}
		printf("\n");
	}
	printf("\n");
}
//x,y表示当前位置,count表示相对于位置(x,y)的8种走法中该走哪一种
int nextxy(int &x, int &y, int count)
{
	//8种选择是x加两格,x退两格与y退一格,y加一格组合,有四种
	//x退一格,x加一格与y加两格,y退两格组合,有四种,一共8种
	switch (count)
	{
	case 0:
		//首先位置要有效,其次该位置没有走过
		if (x + 2 <= X - 1 && y + 1 <= Y - 1 && chess[x + 2][y + 1] == 0)
		{
			//如果有效位置,则将(x,y)改为马下一次要到的位置
			x += 2;
			y += 1;
			return 1;
		}
		break;
	case 1:
		//首先位置要有效,其次该位置没有走过
		if (x + 2 <= X - 1 && y -1 >= 0 && chess[x + 2][y - 1] == 0)
		{
			//如果有效位置,则将(x,y)改为马下一次要到的位置
			x += 2;
			y -= 1;
			return 1;
		}
		break;
	case 2:
		//首先位置要有效,其次该位置没有走过
		if (x - 2 >= 0 && y - 1 >= 0 && chess[x - 2][y - 1] == 0)
		{
			//如果有效位置,则将(x,y)改为马下一次要到的位置
			x -= 2;
			y -= 1;
			return 1;
		}
		break;
	case 3:
		//首先位置要有效,其次该位置没有走过
		if (x - 2 >= 0 && y + 1 <= Y-1 && chess[x - 2][y + 1] == 0)
		{
			//如果有效位置,则将(x,y)改为马下一次要到的位置
			x -= 2;
			y += 1;
			return 1;
		}
		break;
	case 4:
		if (x + 1 <= X - 1 && y + 2 <= Y - 1 && chess[x + 1][y + 2] == 0)
		{
			x += 1;
			y += 2;
			return 1;
		}
		break;
	case 5:
		if (x + 1 <= X - 1 && y - 2 >= 0 && chess[x + 1][y - 2] == 0)
		{
			x += 1;
			y -= 2;
			return 1;
		}
		break;
	case 6:
		if (x - 1 >= 0 && y + 2 <= Y - 1 && chess[x - 1][y + 2] == 0)
		{
			x -= 1;
			y += 2;
			return 1;
		}
		break;
	case 7:
		if (x - 1 >= 0 && y - 2 >= 0 && chess[x - 1][y - 2] == 0)
		{
			x -= 1;
			y -= 2;
			return 1;
		}
		break;
	default:
		break;
	}
	//返回0,需要重新寻找
	return 0;
}
//遍历算法
//x,y表示位置,tag表示当前走了几格,第一次开始时该值应为1
int TravelChess(int x, int y, int tag)
{
	chess[x][y] = tag;
	//如果走了X*Y格,说明全部遍历完,打印棋盘
	if (tag == X*Y)
	{
		print();
		return 1;//返回1表示遍历完成
	}
	//如果没有遍历完
	int x1 = x, y1 = y;
	//寻找下一个有效位置
	int count = 0;
	int flag = nextxy(x1, y1, count);
	//不断自增count,直到找到下一个有效位置
	while (flag == 0 && count <= 7)
	{
		count++;
		flag = nextxy(x1, y1, count);
	}
	//如果找到了下一个有效位置,该有效位置作为(x,y),继续调用自身进行相同的搜索
	while (flag)
	{
		//如果遍历全部完成逐层返回1;
		if (TravelChess(x1, y1, tag + 1))
		{
			return 1;
		}
		//否则说明从该次的(x,y)往后走没有走通,重新寻找该次调用的下一个有效位置
		x1 = x, y1 = y;
		count++;
		flag = nextxy(x1, y1, count);
		while (flag == 0 && count <= 7)
		{
			count++;
			flag = nextxy(x1, y1, count);
		}
	}
	//如果没有找到有效位置,则将棋盘该位置重新赋值为0
	if (0 == flag)
	{
		chess[x][y] = 0;
	}
	//返回0代表这种走法不通,让上一层调用重新寻找有效位置
	return 0;
	
}
int main()
{
	//记录开始时间
	clock_t start = clock();
	//以防万一,初始化棋盘都为0
	for (int i = 0; i < Y; i++)
	{
		for (int j = 0; j < X; j++)
		{
			chess[i][j] = 0;
		}
	}
	printf("请输入马的初始位置:");
	int x, y;
	scanf("%d,%d", &x, &y);
	if (!TravelChess(x, y, 1))
		printf("马踏棋盘失败了,请更换起始位置!\n");
	clock_t end = clock();
	printf("本次运行一共用时:%f", (double)(end - start) / CLOCKS_PER_SEC);

	return 0;
}

用深度优先搜索写的这个实在是太慢了,跑了很久不出结果,我也很绝望,于是就将棋盘换成了55的试了一下很快就出结果了!88的跑不出来,不知道自己的代码有没有问题,用5*5的试了一下,感觉没啥问题。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/junya_zhang/article/details/83444386