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
這次魔王汲取了上次的教訓,把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:
對於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; }