POJ 1753

描述

4*4的方格棋盤,每個棋子有白麪和黑麪,每個方格中的棋子爲白/黑,每輪翻3~5個棋子改變它們的顏色,每輪根據以下規則選擇翻面的棋子:
1. 選擇16個棋子中的任一個
2. 翻轉已選棋子的鄰近棋子(左、右、上、下,四個鄰近的棋子,如果有就翻)
示例:

這裏寫圖片描述

bwbbwwbwbwwwwwbb

b爲黑,w爲白,若翻第三行第一個,則棋盤變成:
bbwwwwwwbwwwwwbb

目標是把所有棋子全都白麪向上或全都黑麪向上,求達成目標的最少回合。

輸入

4行,每行4個字符,”w”或”b”,表示給定的棋盤當前狀態

輸出

從棋盤當前狀態達成目標所需的最少遊戲回合,若當前已達成目標,輸出0,若不可能實現,則輸出”Impossible”(不輸出引號)

樣例輸入

bwwb
bbwb
bwwb
bwww

樣例輸出

4

參考

(1)bfs:http://changgongxiaorong.cn/?p=23
(2)dfs:http://blog.csdn.net/freezhanacmore/article/details/8142014
(3)位運算:http://www.it610.com/article/1987880.htm
根據參考1參考2用廣度搜索和深度搜索都可。

c++實現

(1)廣度搜索版

參考1的思路:

廣度搜索+枚舉+位運算
一個棋子有兩種情況,16個棋子,即棋盤有2^16方種情況,即65536種情況,正好一個短整形,所以想到用一個短整形記錄棋盤情況,16個位對應棋盤16格,0和1表示黑和白。當狀態爲0或65535時表示說明全是‘b’或‘w’.
廣度搜索使用非遞歸模式,速度快,用隊列實現,元素短整形。全部遍歷一遍,剪枝掉重複搜索的情況。

這裏寫圖片描述

代碼:
532K 16MS

#include <iostream>
using namespace std;

const int MAX = 65535;//[0, 65535]共65536種情況

unsigned short myqueue[MAX];//用‘位’保存狀態的數組
int myrear = 0, mytop = 0;// 頭尾指針
bool flag[MAX];//是否出現過
int num[MAX];//記錄翻拍次數
/**
初始化,w爲0,b爲1,拼接成16位數字
*/
void init(){
    unsigned short temp = 0;
    char c;
    for (int i = 0; i < 16; ++i)
    {
        cin >> c;
        //1左移i位,爲當前黑色位置,temp爲現有棋盤,temp不斷與新數字按位與,生成倒序的棋盤
        if(c == 'b'){
            temp |= (1 << i);
        }
    }
    myqueue[myrear++] = temp; //追加新的狀態
    flag[temp] = true; //該狀態已經有過
}
/**now爲當前狀態,i爲要翻轉的棋子序號[0,15]*/
unsigned short change(unsigned now, int i){
    unsigned short temp = 0;
    temp |= (1 << i); //temp爲當前要改變顏色的位置
    // 翻轉相鄰的四個棋子
    if((i+1)%4 != 0){ //該棋子不是最右邊
        temp |= (1 << (i+1));
    }
    if(i%4 != 0){ // 不是最左邊
        temp |= (1 << (i-1));
    }
    if(i > 3){ // 不是最上邊
        temp |= (1 << (i-4));
    }
    if(i < 12){ // 不是最上面
        temp |= (1 << (i+4));
    }
    return now ^ temp; // 舊狀態和新狀態相同爲0,不同爲1
}

bool bfs(){
    // 遍歷現有的狀態
    while(myrear > mytop){
        // 前一個狀態
        unsigned short former = myqueue[mytop++];
        for (int i = 0; i < 16; ++i)
        {
            unsigned short temp = change(former, i);
            if(former == 0 || former == 65535){
                cout << num[former]; // 全黑或全白了
                return true;
            }
            else if (!flag[temp]) //狀態未出現過,剪枝
            {
                myqueue[myrear++] = temp;
                flag[temp] = true;
                num[temp] = num[former] + 1; // 每個記錄的是當前狀態是總共經歷了多少次翻牌次數
            }
        }
    }
    return false;
}

int main()
{
    init();
    if(!bfs()){
        cout << "Impossible";
    }
    return 0;
}

(2)深度搜索版

參考2的思路:
每次遇到下一個方格有兩種處理方法,要麼翻轉,要麼不翻轉。
若不翻轉方格,則直接處理下一個方格,翻轉的方格數不再增加,
若翻轉當前方格,則還要翻轉相鄰的四個方格,然後再處理下一個方格,翻轉的總數+1。

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