【BFS/哈希/队列】问题 D: 【宽搜入门】魔板

该文讨论的是如何通过编程计算从魔板的基本状态到目标状态的最短操作序列,使用BFS(广度优先搜索)算法,并给出了具体的操作如交换行、插入列和旋转等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

在成功地发明了魔方之后,鲁比克先生发明了它的二维版本,称作魔板。这是一张有8个大小相同的格子的魔板:

1 2 3 4

8 7 6 5

我们知道魔板的每一个方格都有一种颜色。这8种颜色用前8个正整数来表示。可以用颜色的序列来表示一种魔板状态,规定从魔板的左上角开始,沿顺时针方向依次取出整数,构成一个颜色序列。对于上图的魔板状态,我们用序列(1,2,3,4,5,6,7,8)来表示。这是基本状态。

这里提供三种基本操作,分别用大写字母“A”,“B”,“C”来表示(可以通过这些操作改变魔板的状态):

“A”:交换上下两行;

“B”:将最右边的一列插入最左边;

“C”:魔板中央四格作顺时针旋转。

下面是对基本状态进行操作的示范:

A:

8 7 6 5

1 2 3 4

B:

4 1 2 3

5 8 7 6

C:

1 7 2 4

8 6 3 5

对于每种可能的状态,这三种基本操作都可以使用。

你要编程计算用最少的基本操作完成基本状态到目标状态的转换,输出基本操作序列。

【输入格式】

输入有多组测试数据

只有一行,包括8个整数,用空格分开(这些整数在范围 1——8 之间),表示目标状态。

【输出格式】

Line 1: 包括一个整数,表示最短操作序列的长度。

Line 2: 在字典序中最早出现的操作序列,用字符串表示,除最后一行外,每行输出60个字符。

Sample Input

2 6 8 4 5 7 3 1

Sample Output

7

BCABCCB

分析:

本题想要找到初始状态的魔板转换到目标状态最快的步骤,故利用BFS思想,由于需要知道每次变换的状态以及变换的步骤,故把状态以及步骤次序作为节点。设置一个队列,把初始状态放入队列中,不断取出,并判断当前状态的可行变换,并保存至队列中,直到找到目标状态。

要点:
  1. 由于需要知道到达目标状态的路径,因此需要把之前的变换步骤一起保存至节点中;

  1. 为了减少队列的级数,因此采取在变换时就判断节点是否到达终点,而不是先放入队列等取出在判断,节省空间;

  1. 由于是在变换时判断节点是否为终点,若是初始状态即为终点,就会出现无法在一开始判断的情况,因此需要在一开始对初始状态进行判断;

  1. 为了减少重复访问状态,利用Hash表记录状态,在变换时判断,若是重复则判断为不可行的变换。

#include <iostream>
#include <queue>
#include <string>
#include <map>

using namespace std;

int en[2][4];
int n[2][4] = {1, 2, 3, 4, 8, 7, 6, 5};
int jui[2][2] = {0, 1, -1, 0};
int juj[2][2] = {1, 0, 0, -1};
char op[3] = {'A', 'B', 'C'};
struct mboard{
    int ju[2][4];
    string s;
    int step;
}be, temp;

int Hash(int a[2][4]){
    int index = 0;
    for(int i = 0; i < 2; i++){
        for(int j = 0; j < 4; j++){
            index *= 10;
            index += a[i][j];
        }
    }
    return index;
}
void Print(int a[2][4]){
    for(int i = 0; i < 2; i++){
        for(int j = 0; j < 4; j++){
            cout << a[i][j] << ' ';
        }
        cout << '\n';
    }
}
void A(int a[2][4]){
    for(int i = 0; i < 2; i++){
        for(int j = 0; j < 4; j++){
            temp.ju[(i + 1) % 2][j] = a[i][j];
        }
    }
}
void B(int a[2][4]){
    for(int i = 0; i < 2; i++){
        for(int j = 0; j < 4; j++){
            temp.ju[i][(j + 1) % 4] = a[i][j];
        }
    }
}
void C(int a[2][4]){
    for(int i = 0; i < 2; i++){
        for(int j = 0; j < 4; j++){
            if(j == 0 || j == 3) temp.ju[i][j] = a[i][j];
            else{
                temp.ju[i + jui[i][j - 1]][j + juj[i][j - 1]] = a[i][j];
            }
        }
    }
}
bool Match(void){
    for(int i = 0; i < 2; i++){
        for(int j = 0; j < 4; j++){
            if(temp.ju[i][j] != en[i][j]) return false;
        }
    }
    return true;
}
void BFS(void){
    temp = be;
    if(Match()){
        cout << temp.step << '\n';
        cout << '\n';
        return;
    }

    map<int, bool> mp;
    mp[Hash(temp.ju)] = true;
    queue<mboard> q;
    q.push(be);

    while(!q.empty()){
        mboard now;
        now = q.front();
        q.pop();

        for(int k = 0; k < 3; k++){
            temp = now;
            switch(k){
                case 0:A(now.ju);
                    break;
                case 1:B(now.ju);
                    break;
                case 2:C(now.ju);
                    break;
            }
            temp.s += op[k];
            temp.step += 1;

            if(Match()){
                cout << temp.step << '\n';
                for(int i = 0; i < temp.s.size(); i++){
                    cout << temp.s[i];
                }
                cout << '\n';
                return;
            }
            if(mp[Hash(temp.ju)] == true) continue;
            else mp[Hash(temp.ju)] = true;

            q.push(temp);
        }
    }
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    for(int i = 0; i < 2; i++){
        for(int j = 0; j < 4; j++){
            be.ju[i][j] = n[i][j];
        }
    }
    be.step = 0;

    while(cin >> en[0][0]){
        for(int j = 1; j < 4; j++){
            cin >> en[0][j];
        }
        for(int j = 0; j < 4; j++){
            cin >> en[1][3 - j];
        }
        BFS();
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值