【轉】AIZU 0121 Seven Puzzle

原博鏈接 https://blog.csdn.net/acm_10000h/article/details/40896075

題目鏈接 http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=0121

題意:7數碼問題。在2×4的棋盤上,擺有7個棋子,每個棋子上標有1至7的某一數字,不同棋子上標的數字不相同。棋盤上還有一個空格(用0表示),與空格相鄰(上下左右)的棋子可以移到空格中,該棋子原先位置成爲空格。給出一個初始狀態(保證可以轉移到最終狀態),找出一種從初始狀態轉變成給定最終狀態的移動棋子步數最少的移動步驟。

輸入:多組輸入,每組8個數,表示初始狀態前四個數爲第一行從左到右,後四個數爲第二行從左到右。 

輸出:至少需要多少步可以從輸入狀態到達最終狀態(0 1 2 3 4 5 6 7) 

(圖1) (圖2)(圖3)
分析:
乍一看這題沒從入手,但只要採取逆向思維,還是可以用廣度優先搜索解決。先不考慮如何用最小步數從輸入狀態到達最終狀態,所有結果的最終狀態都是(01234567),那麼反過來想,只要求出最終狀態到達所有結果時的最小步數並記錄下來,接下來就是查表了。0表示空位置,對空位置周圍的格子採用廣度優先的方式移動到0,並記錄下最小步數的結果即可。如上圖所示,圖1,可以選擇讓7移動過來變成圖2,也可以選擇讓2移動過來變成圖3。我們要做的只不過是不斷重複這種選擇,直至窮舉所有情況並記錄結果。
我主要是用一個map<string, int>來表示(01234567)到string 的最小步數int,只要當前結果還不存在,就加入map,必然能夠窮盡狀態。另外,在移動格子問題上,因爲我採用string來表示當前狀態,那麼移動方向上下左右分別就是當前位置-4, +4, -1, +1。需要注意的是,位置3不能移動到位置4.

思路:與前幾題的bfs不同,這次的bfs沒有明確的移動對象,看似任意一個數都可以當成對象移動。這時我們只需要抓住一個格子就行,比如我們把0作爲移動對象,那麼0在地圖中漫遊所有的格子得到的肯定就是問題的解空間。由於題目的輸入是多個case,如果對每個case都運行一遍bfs就會TLE。這時我們祭出dp技能,只需要一次bfs就將解空間算出來,以後每個case只要到解空間中去找就行了。

代碼一

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

using namespace std;

map<string, int> dp;
int direction[4] = { 1, -1, 4, -4 };

//************************************
// Method:    bfs
// FullName:  bfs
// Access:    public 
// Returns:   void
// Qualifier: 讓0漫遊整個字串
//************************************

void bfs()

{

    queue<string> que;
    que.push("01234567");
    dp["01234567"] = 0;
    while (!que.empty())
    {
        string now = que.front(); que.pop();
        // p是'0'的位置
        int p = 0;

        for (int j = 0; j < 8; ++j)
        {
            if (now[j] == '0')
            {
                p = j;
                break;
            }
        }
 

        for (int i = 0; i < 4; ++i)
        {
            int n = p + direction[i];
            if (0 <= n && n < 8 && 
                !(p == 3 && i == 0) && // 右上角不能再往右了
                !(p == 4 && i == 1))   // 左下角不能再往左了
            {
                string next = now;
                swap(next[p], next[n]);
                if (dp.find(next) == dp.end())
                {
                    dp[next] = dp[now] + 1;
                    que.push(next);
                }
            }
        }
    }
}
 

///////////////////////////SubMain//////////////////////////////////

int main(int argc, char *argv[])
{

    bfs();
    string line;

    while (getline(cin, line))
    {
        line.erase(remove(line.begin(), line.end(), ' '), line.end());
        cout << dp[line] << endl;
    }
    return 0;

}

///////////////////////////End Sub//////////////////////////////////

代碼二

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

using namespace std; 

typedef pair<string, int> P; 

const int INF = 100000000;

//輸入
string a;

//移動方向
int op[4] = {-1, 1, -4, 4};


map<string, int> dp;                        //保存從string變到"01234567"的int


//計算從"01234567"轉換到其他序列所需的最小步數
void bfs(){

    //初始化
    queue<P> que;
    que.push(P("01234567", 0));
    dp["01234567"] = 0;

    //寬度優先搜索
    while(!que.empty()){
        P p = que.front();
        que.pop();
        string s = p.first;
        int cur = p.second;

        for(int i = 0; i < 4; i ++){
            //構造下一次交換
            int next = cur + op[i];
            string str = s;
            swap(str[cur], str[next]);
            map<string, int>::iterator it = dp.find(str);
            //判斷是否可移動以及是否訪問過
            if(0 <= next && next < 8 
                && !(cur == 3 && next == 4) && !(cur == 4 && next == 3) 
                && it == dp.end()){

                que.push(P(str, next));
                dp[str] = dp[s] + 1;
            }
        }
    }
}

void solve(){

    //刪除空格
    a.erase(remove(a.begin(), a.end(), ' '), a.end());
    cout<<dp[a]<<endl;
}

int main(int argc, char const *argv[]){

    //先逆向構造所有情況,後直接讀取輸入用例的結果
    bfs();

    while(getline(cin, a)){
        solve();
    }
    return 0;
}

用find函數來定位數據出現位置,它返回的一個迭代器,當數據出現時,它返回數據所在位置的迭代器,如果map中沒有要查找的數據,它返回的迭代器等於end函數返回的迭代器。 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章