B - 迎娶公主(bfs+状压)

传送门
题目:
多灾多难的公主又被大魔王抓走啦!国王派遣了第一勇士蒜头君去拯救她。身为超级厉害的术士,同时也是蒜头君的好伙伴,你决定祝他一臂之力。你为蒜头君提供了一张大魔王根据地的地图,上面标记了蒜头君和公主所在的位置,以及一些不能够踏入的禁区。你还贴心地为蒜头君制造了一些传送门,通过一个传送门可以瞬间转移到任意一个传送门,当然蒜头君也可以选择不通过传送门瞬移。传送门的位置也被标记在了地图上。此外,你还查探到公主所在的地方被设下了结界,需要集齐 K(0≤K≤5) 种宝石才能打开。当然,你在地图上也标记出了不同宝石所在的位置。你希望蒜头君能够带着公主早日凯旋。于是在蒜头君出发之前,你还需要为蒜头君计算出他最快救出公主的时间。地图用一个 R×C 的字符矩阵来表示。字符 S 表示蒜头君所在的位置,字符 E 表示公主所在的位置,字符 # 表示不能踏入的禁区,字符 “$” 表示传送门,字符 . 表示该位置安全,数字字符 0 至 4 表示了宝石的类型。蒜头君每次可以从当前的位置走到他上下左右四个方向上的任意一个位置,但不能走出地图边界。蒜头君每走一步需要花费 1 个单位时间,从一个传送门到达另一个传送门不需要花费时间。当蒜头君走到宝石所在的位置时,就视为得到了该宝石,不需要花费额外时间。

输入格式
第一行是一个正整数 T(1≤T≤10),表示一共有 T 组数据。 每一组数据的第一行包含了三个用空格分开的正整数 R、C(2≤R,C≤200) 和 K,表示地图是一个 R×C的矩阵,而蒜头君需要集齐 K 种宝石才能够打开拘禁公主的结界。 接下来的 R 行描述了地图的具体内容,每一行包含了 C 个字符。字符含义如题目描述中所述。保证有且仅有一个 S 和 E。$ 的数量不超过 10 个。宝石的类型在数字 0 至 4 范围内,即不会超过 5 种宝石。

输出格式
对于每一组数据,输出蒜头君救出公主所花费的最少单位时间。若蒜头君无法救出公主,则输出 “oop!”(只输出引号里面的内容,不输出引号)。每组数据的输出结果占一行。

Sample Input

1
7 8 2
........
..S..#0.
.##..1..
.0#.....
...1#...
...##E..
...1....

Sample Output
11
状压内容可以参照状压dp自行学习
注意关于scanf输入字符会将回车储存,要解决这个问题可以使用cin

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=10000;
char a[210][210];
int door[10][2];//记录传送门的位置 
int to[4][2]={-1,0,1,0,0,-1,0,1};
int f[210][210][40];//所走位置的状态,最后一个为宝石种类数,运用二进制储存 
int r,c,k,men,sum;//传送门数量,宝石数量 
struct node{
	int x,y;//坐标 
	int step;//步数 
	int kind;//宝石种类 
	node(int xx,int yy,int ss,int kk){
		x=xx;y=yy;step=ss;kind=kk;
	}
};
bool check(int kk){//检查宝石种类是否足够 
	int s=0;
	for(int i=0;i<5;i++){
		if((kk>>i)&1) s++;
	}
	if(s>=k)return true;
	return false;
}
int bfs(int x,int y){
	queue<node>q;
	f[x][y][0]=1;
	q.push(node(x,y,0,0));
	while(!q.empty()){
		node aa=q.front();
		q.pop();
		for(int i=0;i<4;i++){
			int xx=aa.x+to[i][0];
			int yy=aa.y+to[i][1];
			int t=aa.step;
			int kk=aa.kind;
//			cout<<xx<<' '<<yy<<' '<<t<<' '<<kk<<endl;
			if(xx<0||yy<0||xx>=r||yy>=c)continue;//判断是否超过地图范围 
			if(f[xx][yy][kk]==1)continue;//此位置已找过 
			if(a[xx][yy]=='#')continue;//此路不通 
			if(a[xx][yy]=='E'){//判断是否能救出公主 
				if(check(kk)) return t+1;
			}
			if(a[xx][yy]>='0'&&a[xx][yy]<='9'){//拿宝石并记录 
				f[xx][yy][kk]=1;
				kk=kk|(1<<(a[xx][yy]-'0'));//储存宝石的类型 
				q.push(node(xx,yy,t+1,kk));
			}
			if(a[xx][yy]=='.'){//可走,放置搜索
				f[xx][yy][kk]=1;
				q.push(node(xx,yy,t+1,kk)); 
			}
			if(a[xx][yy]=='$'){//判断传送门的位置 
				f[xx][yy][kk]=1;
				q.push(node(xx,yy,t+1,kk));
				for(int j=0;j<men;j++){//到达不同传送门 
					xx=door[j][0];
					yy=door[j][1];
					if(f[xx][yy][kk]==0){
						f[xx][yy][kk]=1;
						q.push(node(xx,yy,t+1,kk));
					}
				}
			}
		}
	} 
	return -1;
}
int main(){
	int t,sx,sy;
	scanf("%d",&t);
	while(t--){
		memset(f,0,sizeof(f));
		sum=0;//记录宝石数量 
		men=0;//记录传送门的数量 
		scanf("%d%d%d",&r,&c,&k);
		for(int i=0;i<r;i++){
			for(int j=0;j<c;j++){
				scanf("%c",&a[i][j]);//不要用scanf,注意两者的区别 !!!
				if(a[i][j]=='S'){
					sx=i,sy=j;
					a[i][j]='.';
				}
				if(a[i][j]=='$'){
					door[men][0]=i;
					door[men][1]=j;
					men++;
				}
				if(a[i][j]>='0'&&a[i][j]<='9')sum++;
			}
		}
		if(k>sum){//如果需要大于实际存在的宝石数量则不可能救出公主 
			cout<<"oop!"<<endl;
			continue;
		}
		int ans=bfs(sx,sy);
		if(ans==-1)cout<<"oop!"<<endl;
		else cout<<ans<<endl;
	}
    return 0;
}
上一篇:leetcode 85. 最大矩形


下一篇:第5章选择语句9、编写程序提示用户输入两个日期,哪个更早