算法之递归回溯(四)

上一篇我们讲解了八皇后的问题:八皇后
这一篇我们将继续聊迷宫棋盘类问题。八皇后问题我们使用一维数组模拟二维数组,以获得较好的效率。接下来,我们将考虑使用二维数组求解迷宫棋盘类问题的解。其中比较典型的是跳马问题

马在中国象棋以日字形规则移动。
请编写一段程序,给定5×5大小的棋盘,以及马的初始位置(0,0),要求不能重复经过棋盘上的同一个点,计算马可以有多少途径遍历棋盘上的所有点。

八皇后问题好像我们讨巧了,我们使用一维数组换来了二维数组的功能。这种事情少之又少,我们不能总是遇到这样的情况。我说过,当二维数组中的某一个位置只有true或者false时,它是二维的信息,用一维数组就够了。但我也很少遇到这样的情况,八皇后是不多见的其中之一。
我们现在终于需要面对二维数组了。二维数组并没有我们想象中那么麻烦,甚至写多了还有一丝轻松,毕竟想这么单纯的思路,在算法里是不多见的。我们来看看吧。
一匹马在棋盘上会跳向何方?我们并不知晓,我们只好列出它的所有可能,我们每一个找一遍,总会找到它合适的位置。在这里插入图片描述
马走日。如果马处于棋盘的正中央,它有8种走法。这些走法该怎么表示呢?这些走法是用该变量表示的。举一个例子,从马(红色)当前位置跳到a处,它是行减少2,列减少1达成的。因此,我们可以使用-2,-1来表示马跳到a的方向。按照这样的方式,我们可以得到以下表格:

方向表示方式
a-2,-1
b-2, 1
c-1, 2
d1, 2
e2, 1
f2,-1
g1,-2
h-1,-2

我们能看到每一个方向由两个数字组成。因此我们可以使用8*2的二维数组表示。8表示8个方向,2表示两个下标的改变,如d[0][0]表示第1个方向中的行下标。同样的道理,d[0][1]表示第1个方向的列下标。这样的话,我们就可以使用循环访问到每一个方向了。
跳马问题要求马跳满每一个格子,那么对于5*5的棋盘来说,跳满25个格子程序就可以结束了。这个作为程序的终止条件,原因是由于跳的方向不同,我们根本不知道结束位置在什么位置,因此我们只能选择跳的次数了。如果在迷宫问题中,终止条件可能就是迷宫的出口了。
与八皇后稍有不同的是,马会在不同位置之间跳动。因此可能会跳到之前走过的位置,为了避免这种情况,我们将跳过的格子标记下来。当下一次跳到这个位置,程序可以直接跳过。
我们尝试描述一下题解的过程。

int arr[5][5] = {};		//棋盘,标记跳过的格子,使用跳的次数进行标记
int d[8][2] = {
	{-2,-1},
	{-2, 1},
	{-1, 2},
	{ 1, 2},
	{ 2, 1},
	{ 2,-1},
	{ 1,-2},
	{-1,-2}
};//八个方向
int total = 0;	//跳法种类

// sx 表示当前位置的行下标
// sy 表示当前位置的列下标
// index 表示当前跳的次数,跳满25次程序结束
void f(int sx, int sy, int index)
{
	if(index > 25)
	{//跳满25次,表明棋盘所有位置都跳满了,跳法种类增加1
		++total;
		return;
	}
	
	//还未跳满
	//并不知道该跳向哪个方向
	//8个方向都试一遍
	for(int i = 0;i < 8; ++i)
	{
		//检查跳向该方向之后的位置是否有效:如是否越界,是否跳过等
		int x = sx + d[i][0];	//跳过之后的行下标
		int y = sy + d[i][1];	//跳过之后的列下标
		if(x >= 0 && x < 5 && y >= 0 && y < 5)
		{//下一个位置未越界
			if(arr[x][y] == 0)	//未跳过的位置
			{
				arr[x][y] = index;	//将该位置标记为跳过了
				f(x, y, index + 1);	//从这个位置向下一个位置跳
				arr[x][y] = 0;	//递归回溯,恢复未跳过的状态
			}
		}
	}
}

int main()
{
	arr[0][0] = 1;	//假设马的起始位置为跳了第一次
	f(0, 0, 2);
	cout << total << endl;
}

这里需要说明的一点是,我们将跳过的位置标记为跳的次数,并将马的初始位置标记为第一次跳。这是为了让跳过的位置保持非0值,让未跳过的位置保持0值。当然,我们也可以选择其他的方式进行标记,这并不是限定的。其中容易遗漏的语句是arr[x][y] = 0,将跳过的位置恢复未跳过的状态。这是后续多种跳法的保证。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我会为您解答这个问题回溯算法是一种常用的解决问题算法递归回溯回溯算法的一种常见实现方式。递归回溯可以用于解决许多问题,比如八皇后、迷宫问题等。 下面以八皇后问题为例,来介绍递归回溯的实现过程。 八皇后问题是指在一个 8×8 的棋盘上放置 8 个皇后,使得每个皇后都不在同一行、同一列以及同一对角线上。现在我们来看看如何用递归回溯来解决这个问题。 1. 定义状态空间:我们可以用一个长度为 8 的一维数组来表示每个皇后所在的列数。 2. 定义决策空间:在每个格子里尝试放置一个皇后,如果满足条件,就继续往下进行,否则就回溯。 3. 定义终止条件:当所有皇后都已经放置完毕时,就找到了一组解。 4. 实现递归回溯: ```python def backtrack(row, queens): # 如果已经找到一组解,则直接返回 if row == 8: print(queens) return # 尝试在当前行的每个位置放置皇后 for col in range(8): # 判断当前位置是否合法 if is_valid(row, col, queens): # 如果合法,则将当前位置加入皇后列表中,并继续尝试下一行 queens.append(col) backtrack(row + 1, queens) # 回溯,将当前位置从皇后列表中删除,继续尝试当前行的下一个位置 queens.pop() def is_valid(row, col, queens): for i in range(row): if queens[i] == col or abs(row - i) == abs(col - queens[i]): return False return True backtrack(0, []) ``` 在上述代码中,我们使用 `queens` 列表来记录每个皇后所在的列数,`row` 表示当前尝试放置的行数。在 `backtrack` 函数中,我们首先判断当前行是否已经放置完毕,如果是,则找到了一组解;否则,我们尝试在当前行的每个位置放置皇后,如果当前位置合法,则继续往下进行,否则就回溯,将当前位置从皇后列表中删除,继续尝试当前行的下一个位置。在 `is_valid` 函数中,我们判断当前位置是否与之前已经放置的皇后冲突,如果冲突,则返回 `False`。 以上就是用递归回溯来解决八皇后问题的过程。在实际应用中,我们可以根据具体问题的特点来定义状态空间、决策空间和终止条件,然后使用递归回溯来求解。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值