题解:洛谷 P1747 好奇怪的游戏
【题目来源】
【题目描述】
爱与愁大神坐在公交车上无聊,于是玩起了手机。一款奇怪的游戏进入了爱与愁大神的眼帘:***(游戏名被打上了马赛克)。这个游戏类似象棋,但是只有黑白马各一匹,在点 \(x_1,y_1\) 和 \(x_2,y_2\) 上。它们得从点 \(x_1,y_1\) 和 \(x_2,y_2\) 走到 \((1,1)\)。这个游戏与普通象棋不同的地方是:马可以走“日”,也可以像象走“田”。现在爱与愁大神想知道两匹马到 \((1,1)\) 的最少步数,你能帮他解决这个问题么?
注意不能走到 \(x\) 或 \(y\) 坐标 \(\le 0\) 的位置。满足此限制的 \((x,y)\) 均可到达。
【输入】
第一行两个整数 \(x_1,y_1\)。
第二行两个整数 \(x_2,y_2\)。
【输出】
第一行一个整数,表示黑马到 \((1,1)\) 的步数。
第二行一个整数,表示白马到 \((1,1)\) 的步数。
【输入样例】
12 16
18 10
【输出样例】
8
9
【核心思想】
-
问题分析:给定两匹马的初始位置 \((x_1, y_1)\) 和 \((x_2, y_2)\),每匹马可以走"日"(8种)或"田"(4种),共12种移动方式,求各自到 \((1,1)\) 的最少步数。不能走到坐标 \(\leq 0\) 的位置。这是一个多源BFS(两次独立BFS)问题,关键在于将特殊移动规则建模为12个方向向量,用BFS求最短路。
-
算法选择:
- BFS(广度优先搜索):无权图最短路,每步代价为1,BFS保证第一次到达目标时步数最少
- 12方向扩展:将"日"字走法(8种)和"田"字走法(4种)统一为12个方向向量
-
关键步骤:
- 方向数组定义:
- "日"字:\((\pm 2, \pm 1), (\pm 1, \pm 2)\) — 8种
- "田"字:\((\pm 2, \pm 2)\) — 4种
- 共12种:
dx[12] = {-2,-2,-1,-1,1,1,2,2,-2,-2,2,2},dy[12] = {-1,1,-2,2,-2,2,-1,1,-2,2,-2,2}
- BFS函数(参数为起始坐标 \((x, y)\)):
- 初始化
dist为 \(-1\)(未访问),dist[x][y] = 0 - 队列扩展12个方向,边界检查(\(1 \leq nx, ny \leq 20\))和访问检查
- 到达 \((1,1)\) 立即返回
dist[nx][ny]
- 初始化
- 两次调用:分别对两匹马的初始位置调用
bfs
- 方向数组定义:
-
时间/空间复杂度:
- 时间复杂度:\(O(2 \times XY \times 12) = O(XY)\),\(X, Y\) 为棋盘范围(本题中 \(20 \times 20\))
- 空间复杂度:\(O(XY)\),
dist数组
-
BFS多方向扩展的核心思想:
- 移动规则统一:将象棋中马的"日"和象的"田"统一为12个方向向量,BFS扩展时无差别处理
- 棋盘边界限制:坐标必须 \(\geq 1\),且题目保证在合理范围内可达,设置上界为 \(20\) 即可覆盖
- BFS最优性保证:无权图中BFS按层扩展,第一次到达目标点的层数即为最少步数
- 独立两次搜索:两匹马互不影响,分别独立运行BFS,无状态共享
- 适用于"网格特殊移动规则最短路"问题,核心在于准确建模所有可能移动方向
【算法标签】
普及 #BFS-二维
【代码详解】
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII; // 定义坐标对类型
const int N = 25; // 定义棋盘大小常量
int dist[N][N]; // 距离数组,存储从起点到每个点的最少步数
// 马的12种移动方式:日字移动和"目"字移动
int dx[12] = {-2, -2, -1, -1, 1, 1, 2, 2, -2, -2, 2, 2};
int dy[12] = {-1, 1, -2, 2, -2, 2, -1, 1, -2, 2, -2, 2};
// 广度优先搜索函数,计算从(x,y)到(1,1)的最少步数
int bfs(int x, int y)
{
memset(dist, -1, sizeof(dist)); // 初始化距离数组为-1,表示未访问
queue<PII> q; // 定义队列用于BFS
dist[x][y] = 0; // 起点到自身的距离为0
q.push({x, y}); // 将起点加入队列
while (!q.empty()) // 当队列不为空时循环
{
int xx = q.front().first, yy = q.front().second; // 取出队首坐标
q.pop(); // 弹出队首
for (int i = 0; i < 12; i++) // 遍历12种移动方式
{
int nx = xx + dx[i], ny = yy + dy[i]; // 计算移动后的新坐标
// 检查新坐标是否越界或已被访问
if (nx < 1 || nx > 20 || ny < 1 || ny > 20 || dist[nx][ny] != -1)
continue;
dist[nx][ny] = dist[xx][yy] + 1; // 更新新坐标的距离
if (nx == 1 && ny == 1) // 如果到达目标点(1,1)
return dist[nx][ny]; // 返回最短距离
q.push({nx, ny}); // 将新坐标加入队列
}
}
}
int main()
{
int x1, x2, y1, y2; // 定义两个起始坐标
cin >> x1 >> y1 >> x2 >> y2; // 输入两个起始坐标
cout << bfs(x1, y1) << endl; // 计算并输出从第一个起点到(1,1)的最少步数
cout << bfs(x2, y2) << endl; // 计算并输出从第二个起点到(1,1)的最少步数
return 0;
}
【运行结果】
12 16
18 10
8
9
浙公网安备 33010602011771号