题目:
问题描述
一个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;
}
结束:
…………