蓝桥杯 试题 算法训练 跳马 C++ 详解

 题目:

问题描述

  一个8×8的棋盘上有一个马初始位置为(a,b),他想跳到(c,d),问是否可以?如果可以,最少要跳几步?

输入格式

  一行四个数字a,b,c,d。

输出格式

  如果跳不到,输出-1;否则输出最少跳到的步数。

样例输入

        1 1 2 3

样例输出

        1

数据规模和约定

  0<a,b,c,d≤8且都是整数。


前言:

关于这道题,在CSDN上有许多解答。这篇文章只讲两种方法,一种是我的解答方法:(最为常见的)DFS(深度搜索),另一种是(我哥的):方法(迭代)。至于其它方法,举一反三,相信不难理解。


开始:

DFS(深度优先):这个应该不用多介绍了吧。就是递归,面临多分支,先选一路到底,然后返回,走另一条,再走完,返回上一级,继续走上一级的其他分支……直到所有分支都走完。

对于马棋的不同走法,寻找最短路径这样的问题。

用DFS遍历所有可能,不断更新最优解即可。

根据题意,联系生活常识可得,马棋走的选择有0~8种:(根据对坐标的+-1,+-2可以得到下一步)

        

 而对DFS较为了解,或看过我之前博客(无聊的逗),就知道对于这八种可能,我们都要去走一走。

每走出一步,可能又是八种选择(也可能到边界或者已经走过,这样选择就少一些)

总结:给定一个点,判断八次,周围的各个点能不能走(能走,就直接转移定点,且标记上一个定点已经走过)不断重复,直到找到目标点。【判断依据:是否出界,是否走过】

 图解:(起始位置:1 1  目标位置:3 3)

 << (我)遍历八分支的顺序,以下是 5 * 5 棋盘的部分样例:

 仅展示了最开始的4种情况,之后会不断回溯(调整到上一状态),即使是 5 * 5 的棋盘也有很多情况,就不一一举例了。不了解dfs的可以自行在纸上(或者excel表格上)推导一下。

【不懂dfs的,可以去看一些算法的教学视频,或者私信问我。一篇文章很难教会一个完全不懂dfs的同学,因为这个它是根据不同题型而变化的,并且还有不少细节以及做题的经验】

 大致流程:

一、准备一个二维数组(棋盘)初始为0;

二、将目标位置(数值)置为2;

三、DFS(走过的置为1,回溯再置为0);

 代码:

#include<iostream>
using namespace std;

int MinSum = 10000;
void dfs(int(*ii)[9], int a, int b, int N = -1)
{
	N++;
	//优化,若"当前步数"比"历史最少步数"要多,
	//证明该走法已经不是最优,也没必要继续递归下去。
	if (N > MinSum) return;
	
	//边界之内
	if (1 <= a && a <= 8 && 1 <= b && b <= 8)
	{
		//到达终点
		if (ii[a][b] == 2 && N < MinSum)
		{
			MinSum = N;
		}
		//未走过
		else if (ii[a][b] == 0)
		{
			//置为1,表示走过
			ii[a][b] = 1;

			//八种走法,全部递归
			dfs(ii, a - 2, b + 1, N); dfs(ii, a - 1, b + 2, N);

			dfs(ii, a + 1, b + 2, N); dfs(ii, a + 2, b + 1, N);

			dfs(ii, a + 2, b - 1, N); dfs(ii, a + 1, b - 2, N);

			dfs(ii, a - 1, b - 2, N); dfs(ii, a - 2, b - 1, N);

			//回溯,将要返回上一级的时候,把走过的位路径消除痕迹
			ii[a][b] = 0;
		}
		else return;	
	}
	else return;
}

int main()
{
	int ii[9][9] = { 0 };

	int a, b, c, d;
	cin >> a >> b >> c >> d;

	ii[c][d] = 2;

	dfs(ii, a, b);

	cout << MinSum;

	return 0;
}

 另一种解法:

递推。

确定数值表示的状态:0 >> 未走过,1 >> 走过,2 >> 终点。

二维数组的初始状态:除去起点1,终点2,其余全为0。

总体思想:遍历所有1,对其八个方向位置进行判断:

若为0,则置为1;若为1或超出边界,则忽略;若为二,则输出结果(循环次数,每次对所有1的遍历为一次)

像病毒从起点(日字形)扩散一样,波及到终点即胜利。

代码:

#include<iostream>
using namespace std;

//自定义数据类型
struct MyInt
{
	int x;
	int y;
};

//判断是否可去
bool func(int a, int b, int x)
{
	if (1 <= a && a <= 8 && 1 <= b && b <= 8 && x != 1)
	{
		return true;
	}
	return false;
}

int main()
{
	int ii[9][9] = { 0 };

	int a, b, c, d;
	cin >> a >> b >> c >> d;

	ii[c][d] = 2;

	int num = 0;
	MyInt mi[200];
	mi[num].x = a;
	mi[num].y = b;
	num++;

	int MinSum = -1;
	int left = 0;

	//马踏八方
	while (1)
	{
		MinSum++;
		int right = num;
		for (int i = left; i < right; i++)
		{
			if (ii[mi[i].x][mi[i].y] == 2)
			{
				cout << MinSum;
				return 0;
			}
			else if (ii[mi[i].x][mi[i].y] == 0)
			{
				ii[mi[i].x][mi[i].y] = 1;
				if (func(mi[i].x - 2, mi[i].y + 1, ii[mi[i].x - 2][mi[i].y + 1]))
				{
					mi[num].x = mi[i].x - 2;
					mi[num].y = mi[i].y + 1;
					num++;
				}
				if (func(mi[i].x - 1, mi[i].y + 2, ii[mi[i].x - 1][mi[i].y + 2]))
				{
					mi[num].x = mi[i].x - 1;
					mi[num].y = mi[i].y + 2;
					num++;
				}

				if (func(mi[i].x + 1, mi[i].y + 2, ii[mi[i].x + 1][mi[i].y + 2]))
				{
					mi[num].x = mi[i].x + 1;
					mi[num].y = mi[i].y + 2;
					num++;
				}
				if (func(mi[i].x + 2, mi[i].y + 1, ii[mi[i].x + 2][mi[i].y + 1]))
				{
					mi[num].x = mi[i].x + 2;
					mi[num].y = mi[i].y + 1;
					num++;
				}

				if (func(mi[i].x + 2, mi[i].y - 1, ii[mi[i].x + 2][mi[i].y - 1]))
				{
					mi[num].x = mi[i].x + 2;
					mi[num].y = mi[i].y - 1;
					num++;
				}
				if (func(mi[i].x + 1, mi[i].y - 2, ii[mi[i].x + 1][mi[i].y - 2]))
				{
					mi[num].x = mi[i].x + 1;
					mi[num].y = mi[i].y - 2;
					num++;
				}

				if (func(mi[i].x - 1, mi[i].y - 2, ii[mi[i].x - 2][mi[i].y - 2]))
				{
					mi[num].x = mi[i].x - 1;
					mi[num].y = mi[i].y - 2;
					num++;
				}
				if (func(mi[i].x - 2, mi[i].y - 1, ii[mi[i].x - 2][mi[i].y - 1]))
				{
					mi[num].x = mi[i].x - 2;
					mi[num].y = mi[i].y - 1;
					num++;
				}
			}
			left = right;
		}
		//cout << endl;
		//for (int i = 1; i <= 8; i++)
		//{
		//	for (int j = 1; j <= 8; j++)
		//	{
		//		cout << ii[i][j] << " ";
		//	}
		//	cout << endl;
		//}
		//system("pause");
	}

	return 0;
}

结束:

…………

评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_Lyz_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值