1. 程式人生 > 其它 >AcWing 1107 魔板

AcWing 1107 魔板

字串\(Hash\),寬搜

三種變化需要點心思,不過還好,用心模擬一下。

#include <bits/stdc++.h>

using namespace std;
const int N = 1e5 + 10;                        //開小了會WA
unordered_map<string, int> dist;               //記錄某個字串,走沒有走過,如果走過,那麼是走了幾步到達的
unordered_map<string, pair<char, string>> pre; //記錄某個字串,它的前序操作符是ABC中的哪一個,它的前序字串是什麼
string start = "12345678";                     //起始態
string ed;                                     //目標態,需要輸入進來
string q[N];                                   //佇列

string A(string t) {
    // 交換上下兩行;
    /* 1 2 3 4        8 7 6 5
                ==>
       8 7 6 5        1 2 3 4
    */
    // 1234 5678---> 8765 4321
    for (int i = 0; i < 4; i++) swap(t[i], t[7 - i]);
    return t;
}

string B(string t) {
    //將最右邊的一列插入到最左邊
    /*
    1 2 3 4     4 1 2 3
            ==>
    8 7 6 5     5 8 7 6
    */
    // 1234 5678 ---> 4123 6785
    // 4向上冒泡,5向後冒泡
    for (int i = 3; i > 0; i--) swap(t[i], t[i - 1]);
    for (int i = 4; i < 7; i++) swap(t[i], t[i + 1]);
    return t;
}

string C(string t) {
    //魔板中央對的4個數作順時針旋轉
    /*
      1 2 3 4     1 7 2 4
              ==>
      8 7 6 5     8 6 3 5
    */
    // 1234 5678 ---> 1724 5368
    /*
    swap(t[1], t[2])   1234 5678  -> 1324 5678  把第一個不匹配的數字放到合適位置上
    swap(t[5], t[6])   1324 5678  -> 1324 5768  把第二個不匹配的數字放到合適位置上
    swap(t[1], t[5])   1324 5768  -> 1724 5368  回到頭,再把第一個不匹配的數字放到合適位置上
    */
    swap(t[1], t[2]), swap(t[5], t[6]), swap(t[1], t[5]);
    return t;
}

void bfs() {
    int hh = 0, tt = -1;
    q[++tt] = start;
    while (hh <= tt) {
        string t = q[hh++];
        if (t == ed) return; //找到即停止

        string m[3];
        m[0] = A(t);
        m[1] = B(t);
        m[2] = C(t);
        for (int i = 0; i < 3; i++) {
            string x = m[i];
            if (!dist.count(x)) { //是不是走過?
                q[++tt] = x;
                dist[x] = dist[t] + 1;
                pre[x] = {'A' + i, t}; // x的前序:t,是通過'A'+i方式轉化過來的
            }
        }
    }
}

int main() {
    char x;
    for (int i = 0; i < 8; i++) cin >> x, ed += x;

    //廣搜
    bfs();

    //輸出距離
    cout << dist[ed] << endl;

    //輸出路徑
    string res;
    //如果操作序列的長度大於0,則在第二行輸出字典序最小的操作序列。
    if (dist[ed]) {
        while (ed != start) {
            res += pre[ed].first; //前序操作符,拼接路徑,是反的,因為從最後的狀態出發,找前序
            ed = pre[ed].second;  //前序字串
        }
        reverse(res.begin(), res.end()); //翻過來
        cout << res << endl;
    }
    return 0;
}