UVa 1601 - The Morning after Halloween(單向BFS版)

時間限制:12.000

題目鏈接:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4476

  論弱渣卡題兩個月做不出來的悲哀……

  從題上來說就是個暴力了點的暴力題。給出一個最大爲16×16的迷宮圖和至多3個ghost的起始位置和目標位置,求最少經過幾輪移動可以使三個ghost都到達目標位置。每輪移動中,每個ghost可以走一步,也可以原地不動,需要注意的是任意兩個ghost不能在相同的位置,因此也不能出現任意兩個ghost對穿,也就是原來是ab,移動之後是ba。每個迷宮圖'#'表示牆,' '表示空地,小寫字母表示ghost的起始位置,大寫字母表示對應ghost的目標位置,比如'a'應該走到'A'。保證任意2×2的空間內都有一個'#'。

  看起來就像是人數多了點的走迷宮嘛,BFS就是了。如果這麼做的話果斷就會超時,因爲在每一個狀態可以走的路太多,3個ghost的話,每個有5個方向可走,3個加起來除去原地不動還有124種走法,而且算出來的最少步數也不一定少,第三組樣例的步數就多達77步,空間開不下,時間花得多。所以這道題就一定得優化了。

  首先是儘量避免搜索不合法的走法。這個時候就是題裏的一個細節,保證任意2×2空間內都有一個‘#’,也就是說,能走的124步裏面有相當多的情況是不合法的。每次都壓到隊列裏面然後不合法再排除就浪費了很多時間,所以這就是優化的入口。這裏用的辦法是把迷宮圖給轉換成了圖,用鄰接表保存起來,這樣搜索的時候只走可以走的點,省去了走‘#’再排除的時間。

  其次,在判重上也可以提高效率。一開始的時候我用了結構體儲存ghost的位置,還動態調整ghost的數量,然後想辦法用哈希判重,結果搞得效率奇慢無比樣例都跑不出來。實際上還是根據任意2×2都有'#'這個細節,可以粗略的估計出整個迷宮中可以走的空地不超過200個,3個ghost的話建一個三維數組,200×200×200=8000000,完全開得下。另外考慮ghost數量不同的問題,這裏想到的方法是把不存在的多餘的ghost放到一個孤立的點中,然後使其起始位置和目標位置相同即可,這樣就避免了需要根據情況動態調整的麻煩。

  靠着上面兩條,已經完全可以A過這題了。

  嗯,那麼思路就是獲得“輸入→建圖→BFS”了。想法是很簡單,寫起來代碼能力差真是個問題,各種卡各種崩各種不對,唉……

  其實這只是部分的優化而已,如果還要提高效率的話,就需要用到雙向BFS了。不過考慮到本渣的水平……先從單向的過度吧,接下來開始改雙向。


#include 
#include 
#include 
#include 
#include 

using namespace std;

int w, h, n;

char pic[20][20]; // 輸入
int num[20][20]; // 輸入中的位置→圖中節點的編號
int vis[200][200][200]; // 標記數組
int connect[200][200]; // 鄰接表
int all; // 圖中節點的數量

int que[10000000][4]; // BFS隊列
int goal[4]; // 目標狀態

inline void BFS() {
    // 初始化
    memset(vis, 0, sizeof(vis));
    int fro = 0, rear = 1;
    vis[que[0][1]][que[0][2]][que[0][3]] = true;

    while(fro < rear) {
        int &step = que[fro][0], &a = que[fro][1], &b = que[fro][2], &c = que[fro][3];
        if(a == goal[1] && b == goal[2] && c == goal[3]) { goal[0] = step; return; }

        for(int i = 0, t1; i <= connect[a][0]; ++i) {
            t1 = (i == 0 ? a : connect[a][i]);
            for(int j = 0, t2; j <= connect[b][0]; ++j) {
                t2 = (j == 0 ? b : connect[b][j]);
                for(int k = 0, t3; k <= connect[c][0]; ++k) {
                    t3 = (k == 0 ? c : connect[c][k]);
                    // 判斷衝突-----
                    if((t1 && t2 && t1 == t2) || (t1 && t3 && t1 == t3) || (t2 && t3 && t2 == t3)) continue; // 不能重合
                    if(t1 && t2 && t1 == b && t2 == a) continue; // t1,t2不能對穿
                    if(t1 && t3 && t1 == c && t3 == a) continue; // t1,t3不能對穿
                    if(t2 && t3 && t2 == c && t3 == b) continue; // t2,t3不能對穿
                    // ----------
                    if(!vis[t1][t2][t3]) {
                        vis[t1][t2][t3] = 1;
                        que[rear][0] = step + 1, que[rear][1] = t1, que[rear][2] = t2, que[rear][3] = t3;
                        ++rear;
                    }
                }
            }
        }

        ++fro;
    }
}

int main() {
    int _t = 0;
    while(scanf("%d%d%d", &w, &h, &n) && w && h && n) {

    // 讀取輸入-----
        gets(pic[0]);
        for(int i = 0; i != h; ++i) gets(pic[i]);
    // ----------

    // 根據輸入建立圖-----
        // 初始化
        memset(connect, 0, sizeof(connect));
        all = 0;
        // 獲得編號
        for(int i = 0; i != h; ++i) for(int j = 0; j != w; ++j) {
            if(pic[i][j] != '#') num[i][j] = ++all;
            else num[i][j] = 0;
        }
        // 建立圖
        for(int i = 0; i != h; ++i) for(int j = 0; j != w; ++j) if(num[i][j]) {
            int &pos = num[i][j];
            if(num[i + 1][j]) connect[pos][++connect[pos][0]] = num[i + 1][j];
            if(num[i - 1][j]) connect[pos][++connect[pos][0]] = num[i - 1][j];
            if(num[i][j + 1]) connect[pos][++connect[pos][0]] = num[i][j + 1];
            if(num[i][j - 1]) connect[pos][++connect[pos][0]] = num[i][j - 1];
        }
    // ----------

    // 尋找初始狀態和目標狀態(測了一下字母範圍只在abc之間所以偷懶就這麼寫了)
        //初始化
        que[0][0] = que[0][1] = que[0][2] = que[0][3] = 0;
        goal[0] = goal[1] = goal[2] = goal[3] = 0;
        // 尋找初始狀態
        for(int i = 0; i != h; ++i) for(int j = 0; j != w; ++j) if(islower(pic[i][j])) {
            if(pic[i][j] == 'a') que[0][1] = num[i][j];
            if(pic[i][j] == 'b') que[0][2] = num[i][j];
            if(pic[i][j] == 'c') que[0][3] = num[i][j];
        }
        // 尋找目標狀態
        for(int i = 0; i != h; ++i) for(int j = 0; j != w; ++j) if(isupper(pic[i][j])) {
            if(pic[i][j] == 'A') goal[1] = num[i][j];
            if(pic[i][j] == 'B') goal[2] = num[i][j];
            if(pic[i][j] == 'C') goal[3] = num[i][j];
        }
    // ----------

        BFS();

        printf("%d\n", goal[0]);
    }
}


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