题目描述
小 A 同学在一个古老的图书馆中,发现了一个经典的拼图游戏。
游戏的规则如下:
拼图由 3×3 的格子组成,其中包含数字 1 到 8 的卡片和一个空格。
只有与空格相邻的卡片可以移动到空格中。
游戏会给出拼图的初始图形和通过移动卡片得到的目标图形。
请你编程计算出,从拼图初始图形到移动卡片得到目标图形,最少需要移动的步数。如果无论如何都无法得到目标图形,则输出字母 N。
比如,下面给出了拼图的初始图形和目标图形。
为了方便输入,我们将拼图上每个位置的状态,按从上到下,从左到右的顺序记录下来,其中空格用 # 代替。
因此上图中的初始图形为:12345#678,目标图形为:1#2453678。
输入
第 1 行输入一个字符串,长度为 9,表示拼图的初始图形。
第 2 行输入一个字符串,长度为 9,表示拼图的目标图形。
输出
输出一个整数,代表最少需要移动步数。如果无论如何都无法得到目标图形,则输出字母 N。
样例
输入
12345#678
1#2453678
输出
2
输入
12364857#
85176423#
输出
26
输入
1234#5678
87#654123
输出
N
说明
样例 1 说明
样例 1 的示意图,请参考题目描述。
可以发现,只需要移动 2 步,就可以从初始图形移动到目标图形。
数据范围
输入数据保证 2 个字符串均包含 1∼8 之间的整数和一个字符 #。
代码1
#include <iostream>
#include <vector>
#include <queue>
#include <unordered_set>
#include <algorithm>
using namespace std;
// 定义拼图状态
struct Puzzle {
string state; // 当前拼图状态
int steps; // 移动步数
};
// 计算拼图的最小移动步数
int minMoveSteps(string start, string target) {
vector<int> dx = {1, -1, 0, 0}; // 上下左右四个方向的偏移量
vector<int> dy = {0, 0, 1, -1};
unordered_set<string> visited; // 记录已经访问过的状态
queue<Puzzle> q; // 广度优先搜索使用的队列
q.push({start, 0}); // 将初始状态入队
visited.insert(start); // 标记初始状态为已访问
while (!q.empty()) {
Puzzle curr = q.front();
q.pop();
if (curr.state == target) {
return curr.steps; // 找到目标状态,返回移动步数
}
int pos = curr.state.find('#'); // 找到空格的位置
int x = pos / 3; // 计算空格所在的行和列
int y = pos % 3;
for (int i = 0; i < 4; i++) {
int nx = x + dx[i]; // 计算新的空格位置
int ny = y + dy[i];
if (nx >= 0 && nx < 3 && ny >= 0 && ny < 3) { // 保证新位置在合法范围内
string nextState = curr.state;
swap(nextState[pos], nextState[nx * 3 + ny]); // 移动卡片,交换空格和相邻卡片的位置
if (visited.find(nextState) == visited.end()) { // 如果新状态没有被访问过
visited.insert(nextState); // 标记新状态为已访问
q.push({nextState, curr.steps + 1}); // 将新状态入队,步数加一
}
}
}
}
return -1; // 无法得到目标状态,返回 -1
}
int main() {
string start, target;
cin >> start >> target; // 输入初始图形和目标图形
int result = minMoveSteps(start, target); // 计算最小移动步数
if (result == -1) {
cout << "N" << endl; // 无法得到目标图形
} else {
cout << result << endl; // 输出最小移动步数
}
return 0;
}
代码2
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// 计算拼图的最小移动步数
int minMoveSteps(string start, string target) {
int startPos, targetPos; // 记录空格的位置
for (int i = 0; i < 9; i++) {
if (start[i] == '#') startPos = i;
if (target[i] == '#') targetPos = i;
}
int startX = startPos / 3; // 计算空格的行和列
int startY = startPos % 3;
int targetX = targetPos / 3;
int targetY = targetPos % 3;
int dx = abs(targetX - startX); // 计算行的移动步数
int dy = abs(targetY - startY); // 计算列的移动步数
return dx + dy; // 返回总的移动步数
}
int main() {
string start, target;
cin >> start >> target; // 输入初始图形和目标图形
int result = minMoveSteps(start, target); // 计算最小移动步数
cout << result << endl; // 输出最小移动步数
return 0;
}
声明
本解析并未参考官方解析,查看官方解析请前往24年东方博宜OJ编程月赛评讲课 - 网易云课堂