1. 程式人生 > >HDU 1429 勝利大逃亡(續)(bfs+狀態壓縮,很經典)

HDU 1429 勝利大逃亡(續)(bfs+狀態壓縮,很經典)

傳送門:

勝利大逃亡(續)

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 10648    Accepted Submission(s): 3860


Problem Description Ignatius再次被魔王抓走了(搞不懂他咋這麼討魔王喜歡)……

這次魔王汲取了上次的教訓,把Ignatius關在一個n*m的地牢裡,並在地牢的某些地方安裝了帶鎖的門,鑰匙藏在地牢另外的某些地方。剛開始Ignatius被關在(sx,sy)的位置,離開地牢的門在(ex,ey)的位置。Ignatius每分鐘只能從一個座標走到相鄰四個座標中的其中一個。魔王每t分鐘回地牢視察一次,若發現Ignatius不在原位置便把他拎回去。經過若干次的嘗試,Ignatius已畫出整個地牢的地圖。現在請你幫他計算能否再次成功逃亡。只要在魔王下次視察之前走到出口就算離開地牢,如果魔王回來的時候剛好走到出口或還未到出口都算逃亡失敗。 Input 每組測試資料的第一行有三個整數n,m,t(2<=n,m<=20,t>0)。接下來的n行m列為地牢的地圖,其中包括:

. 代表路
* 代表牆
@ 代表Ignatius的起始位置
^ 代表地牢的出口
A-J 代表帶鎖的門,對應的鑰匙分別為a-j
a-j 代表鑰匙,對應的門分別為A-J

每組測試資料之間有一個空行。 Output 針對每組測試資料,如果可以成功逃亡,請輸出需要多少分鐘才能離開,如果不能則輸出-1。 Sample Input 4 5 17 @A.B. a*.*. *..*^ c..b* 4 5 16 @A.B. a*.*. *..*^ c..b* Sample Output 16 -1 Author LL Source Recommend linle   |   We have carefully selected several similar problems for you:  
1253
 1072 1728 1401 1195  分析: 這個題目別之處在於還有鎖住的門和鑰匙, 要經過這張門,得先拿到這張門的鑰匙

對於a-j 10把鑰匙,我們共有1024種可能

因此,我們可以採用二進位制來記錄鑰匙的集合

//返回新的鑰匙集合 
//引數:原始的鑰匙集合 獲得的鑰匙的編號
inline int get_key(int key,int num) {
    return key | (1 << num);
}

//返回是否存在門的鑰匙
//引數:鑰匙集合 門的編號
inline bool has_key(int key,int num) {
    return (key & (1
<< num)) > 0; }

所以一共有3層: dis[max_v][max_v][1024]

有這麼多種狀態,仔細想想就是站在不同的點擁有不同鑰匙集合的狀態

注意遇到小寫字母的時候記得進行左移 再與前一個狀態值進行或運算,例如,假設已經用了A 門的要是,狀態此時因該是0000000001,意思是擁有了a,如果下一次遇到了J門的鑰

匙,也就是j,那就應該是(1<<10) | (0000000001),那麼此時的狀態應該是1000000001,當遇到已經擁有鑰匙的門的時候再進行右移運算,例如下一次遇到J門時,我們應該先將

1000000001右移10位再與 1進行(&)與運算,如果擁有J門的鑰匙 應該是1&1=1 ,是真值,可以通過,如果沒有,則0&1=0,是假值,則無法通過。

其餘的跟普通的bfs都是一樣的

需要注意的地方:

1.越界直接返回

2.遇到了某個鑰匙,要生成新的鑰匙集合

3.遇到了門,要檢查當前鑰匙集合能不能開啟次門,不能開啟就往另外一個方向搜

4.門能開啟的話且這種狀態沒有搜過的話,步數加一

5.如果滿足了上面條件,但是超時了,也要重新搜

還有一共很重要的一點

就是搜到了終點的時候,這個返回某值然後結束函式的程式碼的位置

原來一直都是放在迴圈的開頭,但這個題目不行,wa了幾次,也不知道怎麼改

我想應該是因為狀態的特殊性,畢竟路上有門!

ps:第一個狀態壓縮的題,紀念一下

具體請參考程式碼

ac程式碼:

#include<bits/stdc++.h>
using namespace std;
#define max_v 25
char G[max_v][max_v];//
int dis[max_v][max_v][1024];//步數
int dir[4][2]= {{-1,0},{0,-1},{1,0},{0,1}}; //方向陣列
int n,m,t;//行,列,限定時間
int sx,sy,fx,fy;//起點和終
struct node
{
    int x,y;
    int key;
    node(int a,int b,int c)
    {
        x=a;
        y=b;
        key=c;
    }
};

inline int get_key(int key,int num)//返回新的鑰匙集合
{
    //引數:元素的鑰匙集合 活動鑰匙的編號
    return key|(1<<num);
}

inline bool has_key(int key,int num)//返回是否存在門的鑰匙
{
    //引數:鑰匙集合 門的編號
    return (key&(1<<num))>0;
}
int bfs()
{
    //初始化
    queue<node> q;
    int step=-1;
    memset(dis,-1,sizeof(dis));

    q.push(node(sx,sy,0));
    dis[sx][sy][0]=0;

    while(!q.empty())
    {
        int x=q.front().x;
        int y=q.front().y;
        int key=q.front().key;
        q.pop();

        for(int i=0; i<4; i++)
        {
            int xx=x+dir[i][0];
            int yy=y+dir[i][1];
            int kk=key;

            if(xx<0||xx>=n||yy<0||yy>=m||G[xx][yy]=='*')//越界和牆
                continue;
            if(G[xx][yy]>='a'&&G[xx][yy]<='j')//遇到了鑰匙
            {
                kk=get_key(kk,G[xx][yy]-'a');//返回新的鑰匙集合
            }
            if(G[xx][yy]>='A'&&G[xx][yy]<='J')//遇到了門
            {
                if(!has_key(kk,G[xx][yy]-'A'))//沒有對應的鑰匙
                {
                    continue;
                }
            }
            if(dis[xx][yy][kk]==-1)
            {
                dis[xx][yy][kk]=dis[x][y][key]+1;//步數加1
                if(dis[xx][yy][kk]>=t)//超過了限度時間
                {
                    continue;
                }
                if(xx == fx && yy == fy)//放這裡是因為路上有門的特殊性
                {
                    step = dis[xx][yy][kk];
                    continue;
                }

                q.push(node(xx,yy,kk));
            }
        }
    }
    return step;
}
int main()
{
    while(~scanf("%d %d %d",&n,&m,&t))
    {
        for(int i=0; i<n; i++)
        {
            for(int j=0; j<m; j++)
            {
                scanf("\n%c",&G[i][j]);
                if(G[i][j]=='@')
                {
                    sx=i;//起點
                    sy=j;
                }
                if(G[i][j]=='^')
                {
                    fx=i;//終點
                    fy=j;
                }
            }
        }
        cout<<bfs()<<endl;
    }
    return 0;
}