1. 程式人生 > >POJ 1753 Flip Game 題解

POJ 1753 Flip Game 題解

POJ 1753 Flip Game 題解

題目解讀

原題摘錄
讀到這個題目,首先要明確題目要求完成的任務。這道題的背景是翻轉棋遊戲。翻轉棋遊戲以4*4的棋盤為背景,有黑白兩種顏色的棋子。現在題目中給出了在某一時刻棋盤上棋子的分佈,要求每一回合選擇棋盤上的某一個座標,通過翻轉規則作用棋盤發生變化。問最少經過幾個回合能使得棋盤上的棋子變成全白或者全黑的狀態。
顯然,題目的重點在翻轉規則:該遊戲中,將選擇修改的座標棋子及其上下左右方向的棋子,共計五個進行翻轉修改(如果上下左右有棋子的話則翻轉,否則不翻轉)。

解決演算法

對題目進行抽象,比較容易想到的就是採用廣度優先演算法(BFS),通過逐層搜尋,達到目標。現在先明確BFS中的幾個關鍵變數。

關鍵變數 本題中的物件
目標狀態 棋盤上達到全白或者全黑
基本狀態 棋盤上當前時刻黑白棋子的分佈
狀態轉移 翻轉規則:變換座標以及上下左右共計五個棋子

第一個問題:如何表示棋盤狀態?
在這裡,我做題時思考了兩種方法,一是直接利用string類字串來記錄整個棋盤,可以將棋盤儲存為一個字元陣列,然後通過函式轉換成為字串;二是觀察到棋盤共計16個位置,剛好是一個short int的長度,因此可以考慮用二進位制壓縮的形式來表示棋盤。事實證明,這種二進位制表示的形式有較快的運算速度,也不會出現超時的情況。

第二個問題:如何進行狀態判重?
在廣搜中,提高搜尋效率,避免程式超時的很重要的方法就是進行剪枝操作,細化之後就是狀態判重。之前在做廣搜的題時,我會選擇將其轉換為整型陣列,然後乘上10的冪次相加得到一個和來表示狀態。這樣做運算量大,而且有的時候計算結果會超出變量表示範圍。這道題可以很容易分析得到棋盤的變化情況共有2^16次方共計65536種可能。一種思路是利用C++ STL中的map結構,向map中新增新的狀態,通過map.find()和map.count()兩種方法來檢驗判重;另一種思路是延續二進位制的方法,通過開闢label[65536]的陣列來標記是否已有該狀態。不過第二種方法可能會浪費儲存空間,但在尋找判重的時間上應該會略快於find()和count()方法。這裡有一個小的trick就是,無論使用map還是label陣列,都可以將它們對應的值用來表示到達這一狀態所需的最少步驟,從而省去了另外開闢變數來記錄的步驟。當然如果要是需要打印出變換路徑,還是需要其他輔助變數的。

第三個問題:如何進行狀態變換?
這裡我以二進位制壓縮的方法作為說明物件。採用二進位制表示的一大優點就在於狀態的變換可以通過二進位制運算來進行。計算機進行二進位制運算是最拿手的。因此在這裡我們只要確定要修改的棋子對應於哪些二進位制位,然後通過取反對應位就可以完成。那麼怎樣做到取反對應位呢?首先,我們要確定哪些位要進行取反。這裡舉一個簡單的例子:假設棋盤從左到右,從上到下依次編號為0,1,2,……,15。二進位制數最右側為No.0,最左側為No.15,則我現在需要對No.4位做翻轉,那麼我需要先將1左移4位。因為1可以表示為 0000 0000 0000 0001b(這是二進位制表示),通過“<<”左移運算子移位四次,能夠將它變換為:0000 0000 0001 0000b,然後通過^取反操作即可完成對應位的取反。如果要同時進行多個位的變換,結合“|”運算即可。

遇到的問題

Problem 1:我在採用map+string的方法時,始終出現Time exceed的情況,個人認為是在呼叫相關string類函式的時候速度較慢,而且map中的判重函式find()和count()可能在這道題裡面與string結合時效率不夠高;(有很大可能是我用錯了。。。)

Problem 2:在使用二進位制壓縮的方法時,對給出的測試樣例能夠通過,但提交時報出結果錯誤。通過比較程式碼等發現,問題出在變換調整的臨界條件上。由於採用二進位制表示,將4*4的棋盤拉成了在一行表示,我錯誤理解為不能取的棋子只會出現在編號小於0和編號大於15的情況。但一定要注意還原到棋盤的時候,對於index+1和index-1編號,極有可能加到下一行或者減到上一行去,這個地方被我疏忽掉了,導致結果錯誤,修改後的調整限制條件為:
這裡寫圖片描述

收穫

這是一個基本的BFS搜尋題,但自己卡的時間還比較長,只能說練習的次數太少。在掌握BFS的模板後應該在很短的時間內就能AC。

程式碼

#include<iostream>
#include<queue>

using namespace std;

int label[65535];
queue<int> Q;

int Adjust(int middle,int index)
{
        int next=middle;
        unsigned short int ted=0;
        ted = ted|(1<<index);
        if(index+4<16){
                ted=ted|(1<<(index+4));
        }
        if(index-4>=0){
                ted=ted|(1<<(index-4));
        }
        if((index+1)%4!=0){
                ted=ted|(1<<(index+1));
        }
        if((index%4!=0)&&(index-1>=0)){
                ted=ted|(1<<(index-1));
        }
        next = next^ted;
        return next;
}

int main()
{
        int i,temp=0;
        int middle,next,finals=-1;
        char c;
        for(i=0;i<65535;i++){
                label[i]=-1;
        }
        for(i=0;i<16;i++){
                cin>>c;
                if(c=='b'){
                        temp = temp|(1<<i);
                }
        }
        label[temp]=0;
        Q.push(temp);
        if(temp==65535||temp==0){
                cout<<0<<endl;
        }
        else{
                while(!Q.empty()){
                        middle=Q.front();
                        Q.pop();
                        for(i=0;i<16;i++){
                                next=Adjust(middle,i);
                                if(next==65535||next==0){
                                        label[next]=label[middle]+1;
                                        finals=label[next];
                                        break;
                                }
                                if(label[next]!=-1){
                                        continue;
                                }
                                else{
                                        label[next]=label[middle]+1;
                                        Q.push(next);
                                }
                        }
                        if(finals!=-1){
                                break;
                        }
                }
                if(finals!=-1){
                        cout<<finals<<endl;
                }
                else{
                        cout<<"Impossible"<<endl;
                }
        }
        return 0;
}