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

原題地址

题目描述

玛雅人有一种密码,如果字符串中出现连续的2012四个数字就能解开密码。给一个长度为N的字符串,(2=<N<=13)该字符串中只含有0,1,2三种数字,问这个字符串要移位几次才能解开密码,每次只能移动相邻的两个数字。例如02120经过一次移位,可以得到20120,01220,02210,02102,其中20120符合要求,因此输出为1.如果无论移位多少次都解不开密码,输出-1。

输入描述:

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

输出描述:

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

分析

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

#include <string>
#include <iostream>
#include <set>
#include <memory>
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<string> status;
        status.insert(str);
        int c;
        for (c = 0; c < 100; c++) {
            set<string> 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(集合)這個數據結構,目的是為了去重。筆者每次都清空集合,然後依次將處理過的字符串按依次放回集合,其效果類似隊列,但附加了去重的功效。
如果要用隊列來做這題的話,就應當採用其它去重的方式,例如使用散列表來記錄字符串出現的次數。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容