回溯法-马的遍历问题

问题描述:

 在中国象棋中,马只能走“日” 字。现有一5*4棋盘,马从位置(x,y)处出发,把棋盘的每一格都走一次,且只走一次。找出所有路径。

问题分析:

     行n,列m,马在不出边界的情况下有8个方向可以行走(走日字),如当前坐标为(x,y),则行走后的坐标可以为:
(x+1,y+2) (x+1,y-2), (x+2,y+1) (x+2,y-1)
(x-1,y-2) (x-1,y+2) (x-2,y-1) (x-2,y+1)

算法设计:

搜索空间是整个棋盘n*m个棋盘上点

约束条件:不出边界且每个点只经过一次。

搜索过程:从任一点(x,y)出发,按深度优先的原则,从8个方向中尝试一个可以走的点,直到走过棋盘上所有n*m个点。

伪代码:

int fx[8]={1,2,2,1,-1,-2,-2,-1};
int fy[8]={2,1,-1,-2,-2,-1,1,2};
int n=5,m=4;
int a[5][4];

main() {
	int i, j;
	count = 0;
	dep = 1;
	print("请输入初始位置(x,y):");
	input(x,y);
	if (x > 5 or y > 4 or x < 1 or y < 1) {		//判断输入是否合法
		print("INPUT ERROR!");
		return 0;
	}
	for (i = 1; i <= 5; i++) {					//a[6][5]初始化置0,表示为遍历过该点
		for (j = 1; j <= 4; j++)
			a[i][j] = 0;
	}
	a[x][y] = 1;								//起始点(x,y)的顺序为1
	
	find(x, y, 2);								//调用Path函数遍历下一点

	if (count == 0)								//输出走法数量
		print("No Answer!");
	else
		print("共有%d种走法\n", count);
	return 0;
}

find(int x,int y,int dep)
{
	int i,xx,yy;
	for(i=0;i<=8;i++)//8个方向试探
	{
		xx=x+fx[i];
		yy=y+fy[i];
		if(check(xx,yy=1))//判断新坐标
		{
			a[xx][yy]=dep;
			if(dep==n*m)//当步数达到n*m时表示已走完每一个节点
			{
				count++;
				output();	
			}
			else
			{
				find(xx,yy,dep+1);//在新点上继续向下探索
			}
			a[xx][yy]=0;//回溯,将走过的点重置为未走过
		}
	}
}

Output() {
	int i, j;
	printf("第%d种走法\n", count);
	for (i = 1; i < 6; i++) {
		for (j = 1; j < 5; j++) {
			print( a[i][j]);
		}
		print("\n");
	}
	print("\n");
}

c语言源代码:

# include<stdio.h>

int dep, x, y, count;							
//dep为搜索的深度即已遍历的点数,(x,y)为初始坐标,count用于统计走法个数
int fx[8] = { 1,2,2,1,-1,-2,-2,-1 };
int fy[8] = { 2,1,-1,-2,-2,-1,1,2 };
int a[6][5];									
//a[6][5]用于存储棋盘每一点,其值代表该点的遍历顺序,数组下标对应棋盘点的坐标

/*输出遍历结果*/
void Output_Path() {
	int i, j;
	printf("第%d种走法\n", count);
	for (i = 1; i < 6; i++) {
		for (j = 1; j < 5; j++) {
			printf("%3d", a[i][j]);
		}
		printf("\n");
	}
	printf("\n");
}

/*遍历下一点,回溯*/
void Path(int x, int y, int dep) {
	int i;
	int xx, yy;
	for (i = 0; i < 8; i++) {					//(xx,yy)为下一步移动的新坐标
		xx = x + fx[i];
		yy = y + fy[i];
		if (xx >= 1 && xx <= 5 && yy >= 1 && yy <= 4 && a[xx][yy] == 0) {	
		//判断新坐标(xx,yy)未出界且为被遍历过
			a[xx][yy] = dep;					//数组a存储当前点的遍历顺序
			if (dep == 20) {					//如果以全部遍历完则输出这种解法
				count++;
				Output_Path();
			}
			else {
				Path(xx, yy, dep + 1);			//没有遍历完则调用Path函数继续遍历下一点
			}
			a[xx][yy] = 0;						//回溯,置0退回
		}
	}
}

int main() {
	int i, j;
	count = 0;
	dep = 1;
	printf("请输入初始位置(x,y):");
	scanf("%d%d", &x, &y);
	if (x > 5 || y > 4 || x < 1 || y < 1) {		//判断输入是否合法
		printf("INPUT ERROR!");
		return 0;
	}
	for (i = 1; i <= 5; i++) {					//a[6][5]初始化置0,表示为遍历过该点
		for (j = 1; j <= 4; j++)
			a[i][j] = 0;
	}
	a[x][y] = 1;								//起始点(x,y)的顺序为1
	
	Path(x, y, 2);								//调用Path函数遍历下一点

	if (count == 0)								//输出走法数量
		printf("No Answer!");
	else
		printf("共有%d种走法\n", count);
	return 0;
}

运行截图:

  • 5
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
回溯法遍历问题的复杂度分析需要考虑以下几个因素: 1. 棋盘的大小:设棋盘大小为 $n \times m$,则遍历问题的规模为 $nm$。 2. 起始位置的选择:由于走法具有对称性,因此可以假设起始位置为 $(0, 0)$,并对其它起始位置进行对称处理。这样可以减少起始位置的选择,但并不会影响问题的复杂度。 3. 走法:每个格子可以选择的下一个格子最多有 $8$ 个,因此每个格子最多需要尝试 $8$ 次才能找到一个可行的下一个格子。 4. 剪枝优化:在实际的实现中,可以使用一些剪枝优化来减少回溯的次数,例如可以按照下一个格子能够到达的未访问过的格子数量从小到大进行排序,这样可以优先选择能够到达较少未访问过的格子的下一个格子。 根据以上因素,回溯法遍历问题的复杂度可以分析如下: - 间复杂度:设棋盘大小为 $n \times m$,则回溯法遍历问题间复杂度为 $O(8^{nm})$,因为每个格子最多需要尝试 $8$ 次才能找到一个可行的下一个格子,而遍历所有格子需要尝试的次数是指数级别的。 - 空间复杂度:回溯法遍历问题的空间复杂度取决于递归调用栈的深度,即最多需要保存 $nm$ 个格子的信息。因此空间复杂度为 $O(nm)$。 需要注意的是,由于回溯法遍历问题间复杂度是指数级别的,因此对于较大的棋盘来说,回溯法是无法在合理的间内得到解决的。在实际应用中,需要考虑使用其它更加高效的算法来解决遍历问题

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值