瑪雅人的密碼【廣度優先搜索、c++】

原題地址

题目描述

玛雅人有一种密码,如果字符串中出现连续的2012四个数字就能解开密码。给一个长度为N的字符串,(2=

输入描述:

输入包含多组测试数据,每组测试数据由两行组成。
第一行为一个整数N,代表字符串的长度(2<=N<=13)。
第二行为一个仅由0、1、2组成的,长度为N的字符串。

输出描述:

对于每组测试数据,若可以解出密码,输出最少的移位次数;否则输出-1。

分析

首先注意到,能解開密碼的充要條件是字符串中包含至少一個0、一個1、兩個2. 其必要性不言自明,其充分性解釋如下:對於字符串中出現的字符,不妨設,其中表示第一個出現的2和第二個出現的2。在這種約定下,使用冒泡排序算法,從小到大排序,我們就能在字符串的頭部見到“2012”這個序列。眾所周知,冒泡排序每次都只移動兩個相鄰的元素,滿足題目中關於“移動”的要求。
於是,對於一個輸入,我們先用上述條件判斷它是否能夠解開密碼。若能,再分析至少要移動多少次可以解開密碼。後面這一步顯然應當運用廣度優先搜索來解決。
代碼如下:

#include 
#include 
#include 
#include 
using namespace std;


bool impossible(string& str) {
    int stat[3] = {0};
    for (int i = 0; i < str.length(); i++) {
        stat[str[i] - '0']++;
    }
    return stat[0] == 0 || stat[1] == 0 || stat[2] < 2;
}


int main() {
    int num;
    string str;
    while (cin >> num >> str) {
        if (impossible(str)) {
            cout << -1 << endl;
            continue;
        }
            
        set status;
        status.insert(str);
        int c;
        for (c = 0; c < 100; c++) {
            set status2 = status;
            status.clear();
            for (int i = 0; i < num - 1; i++) {
                for(string s: status2) {
                    if (s.find("2012") != string::npos)
                        goto report;
                    string s2 = s;
                    swap(s2[i], s2[i + 1]);
                    status.insert(s2);
                }
            }
        }
        report: cout << c << endl;
    }
}

與尋常思路不同的是,筆者沒有採用隊列來做廣度優先搜索,而是用了set(集合)這個數據結構,目的是為了去重。筆者每次都清空集合,然後依次將處理過的字符串按依次放回集合,其效果類似隊列,但附加了去重的功效。
如果要用隊列來做這題的話,就應當採用其它去重的方式,例如使用散列表來記錄字符串出現的次數。

你可能感兴趣的:(瑪雅人的密碼【廣度優先搜索、c++】)