东方博宜24年7月-B组(才俊)拼图游戏

题目描述

小 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编程月赛评讲课 - 网易云课堂

  • 20
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值