題面:
題目中文大意:
這個故事發生在“星際迷航”的背景下。
“星際爭霸”的副隊長史波剋落入克林貢的詭計中,被關押在他們的母親星球Qo’noS上。
企業的上尉詹姆斯·T·柯克(James T. Kirk)不得不乘宇宙飛船去救他的副手。幸運的是,他偷走了史波克所在的迷宮地圖。
迷宮是一個矩形,它有n行垂直和m列水平,換句話說,它被分爲n * m個位置。有序對(行號,列號)表示迷宮中的位置。柯克從當前位置移動到下一個花費1秒。而且他只有在以下情況下才能移動到下一個位置:
下一個位置與當前柯克的位置相鄰(上下或左右)(4個方向)
開着的門是可以通行的,但鎖着的門不是。
柯克不能通過一堵牆
有幾種門是默認鎖定的。鑰匙只能打開相同類型的門。柯克必須在打開相應的門之前拿到鑰匙,這樣很浪費時間。
柯克的初始位置是(1,1),而史波克位於(n,m)的位置。你的任務是幫助Kirk儘快找到史波克。
Input
輸入包含很多測試樣例。
每個測試樣例由幾行組成。第一行中有三個整數,分別代表n,m和p (1<= n, m <=50, 0<= p <=10).
第二行只列出一個整數k,表示門和牆的總數(0<= k <=500).
在下面的k行中有5個整數,表示 i1, y i1, x i2, y i2, g i; 當g i >=1,表示在位置 (x i1, y i1) 和 (x i2, y i2)之間存在類型gi的門;當g i = 0,說明 (x i1, y i1)和 (x i2, y i2),之間存在一堵牆 ( | x i1 - x i2 | + | y i1 - y i2 |=1, 0<= g i <=p )
下面的行是一個整數S,表示迷宮中的鑰匙的總數。(0<= S <=50).
在下面的S行中有三個整數,分別表示x i1, y i1和q i這意味着類型爲qi的鑰匙位於位置 i (x i1, y i1), (1<= q i<=p).
Output
輸出Kirk可能達到史波克的可能最小的秒數。
如果沒有可能的計劃,輸出-1。
Sample Input
4 4 9
9
1 2 1 3 2
1 2 2 2 0
2 1 2 2 0
2 1 3 1 0
2 3 3 3 0
2 4 3 4 1
3 2 3 3 0
3 3 4 3 0
4 3 4 4 0
2
2 1 2
4 2 1
Sample Output
14
分析:
此題應使用廣度優先搜索(BFS)
狀態壓縮:有10種鑰匙,那麼我們用10位二進制來存儲鑰匙串(即一個int整型變量),第i-1位是1表示有第i種鑰匙,爲0則沒有鑰匙
用位運算來判斷是否能通過門(這些操作很常用,應牢記)
將第i種鑰匙加入鑰匙串key
代碼:key|=(1<<(i-1))
例:key=0010 ,i=4,1<<(i-1)=1000,0010|1000=1010拿着鑰匙串key,是否能通過第i種門
代碼:if(key&(1<<(i-1)==0) continue
例:(沒拿到第5種鑰匙,想通過第5種門)key=01101,i=5,1<<(i-1)=10000,00101|10000=0,通不過,continue
(拿到第4種鑰匙,想通過第4種門)key=01101,i=4,1<<(i-1)=01000,00101|10000=01000,可通過
代碼:
#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 56
#define maxqu 50*50*(1<<11)+10
using namespace std;
int n,m,p,k,s;
struct node{
int x;
int y;
int step;
int key;//存儲鑰匙
};
node now,nex;
node queue[maxqu];
int door[maxn][maxn][maxn][maxn];//鄰接矩陣存門
int keys[maxn][maxn];//二維數組存儲每一個點的鑰匙
int used[maxn][maxn][2055];//標誌是否走過
const int walkx[4]={1,-1,0,0},walky[4]={0,0,1,-1};
int fread(){//快速輸入
int x=0;
char c=getchar();
int sign=1;
while(c<'0'||c>'9') {
if(c=='-') sign=-1;
c=getchar();
}
while(c>='0'&&c<='9') {
x=x*10+c-'0';
c=getchar();
}
return x*sign;
}
bool judge(int x1,int y1,int x2,int y2){
if(door[now.x][now.y][nex.x][nex.y]==0) return false;//有牆
if(x2>=1&&x2<=n&&y2>=1&&y2<=m) return true;
else return false;
}
int bfs(){
int head=0,tail=0;
int tmp;
queue[0].x=1;
queue[0].y=1;
queue[0].step=0;
queue[0].key|=keys[1][1];//起點有鑰匙的特判
memset(used,0,sizeof(used));
used[1][1][queue[0].key]=1;
do{
now=queue[head];
if(now.x==n&&now.y==m) return now.step;
for(int i=0;i<4;i++){
nex.x=now.x+walkx[i];
nex.y=now.y+walky[i];
nex.key=now.key;
nex.step=now.step;
if(judge(now.x,now.y,nex.x,nex.y)){
if(door[now.x][now.y][nex.x][nex.y]>0){//開門
tmp=door[now.x][now.y][nex.x][nex.y]-1;
if((nex.key&(1<<tmp))==0) continue;
}
if(keys[nex.x][nex.y]>0){//拿鑰匙
nex.key|=keys[nex.x][nex.y];
}
if(used[nex.x][nex.y][nex.key]==1) continue;
used[nex.x][nex.y][nex.key]=1;
tail++;
queue[tail].x=nex.x;
queue[tail].y=nex.y;
queue[tail].key=nex.key;
queue[tail].step=nex.step+1;
}
}
head++;
}while(head<=tail);
return -1;
}
int main(){
// freopen("data.txt","r",stdin);
int x1,y1,x2,y2,v;
while(cin>>n>>m>>p){
memset(door,-1,sizeof(door));
memset(keys,0,sizeof(keys));
k=fread();
for(int i=1;i<=k;i++){
x1=fread();
y1=fread();
x2=fread();
y2=fread();
v=fread();
door[x1][y1][x2][y2]=v;//>0門,=0牆,-1無
door[x2][y2][x1][y1]=v;
}
cin>>s;
for(int i=1;i<=s;i++){
x1=fread();
y1=fread();
v=fread();
keys[x1][y1]|=(1<<(v-1));//預處理鑰匙,注意一個位置有多個鑰匙的情況
}
cout<<bfs()<<endl;
}
}