算法之递归回溯(四)

这篇博客探讨了如何使用二维数组解决5x5棋盘上的跳马问题,从初始位置(0,0)开始,确保马不重复经过任何点,计算出所有可能的遍历路径数量。通过定义马的8个可行跳跃方向,利用递归搜索每个可能的路径,并在棋盘上标记已访问过的点,最终计算得出总路径数。程序在遍历25次(即棋盘所有点)后结束。这个算法展示了如何处理二维数组以及在回溯过程中恢复状态的重要性。

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

马在中国象棋以日字形规则移动。
请编写一段程序,给定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,将跳过的位置恢复未跳过的状态。这是后续多种跳法的保证。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值