计蒜客 T-1215-拯救公主(bfs+三维数组标记+二进制压缩)

拯救公主

题目描述:

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

输入:

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

输出:

对于每一组数据,输出蒜头君救出公主所花费的最少单位时间。
若蒜头君无法救出公主,则输出 “oop!”(只输出引号里面的内容,不输出引号)。
每组数据的输出结果占一行。
输出时每行末尾的多余空格,不影响答案正确性

思路:

  1. 传送门应首先保存下来,搜索过程中,使用或不使用在去判断
  2. 在到达终点时,宝石的种类需要大于等于k才可以,因此搜索时还需要去寻找宝石,而如果使用二维数组标记的话,走过的就不能再走了,如果两个宝石不在一条路上,这个宝石取走之后,就不能原路返回,另一个宝石也就无法获得,因此需要使用三维数组标记。
  3. 对于宝石的存储,这里使用二进制状态压缩,最多五种宝石,用11111表示五种宝石,1的数量就是宝石种类数量。
    (k>>i)&i 取出k二进制的第i位
    k=k|1<<i,表示将tk的第i位赋为1

代码:

#include <iostream>
#include <cstring>
#include <queue> 
using namespace std;
int r,c,k;
char a[205][205];
int s[10][2];//传送门
int flag[205][205][35];
int tr[4][2]={{-1,0},{1,0},{0,-1},{0,1}};
int t,sum;//传送门 宝石 数量 
struct node
{
	int x;
	int y;
	int s;//步数 
	int key;//宝石种类 
	node(int xx,int yy,int ss,int kk)
	{
		x=xx;y=yy;s=ss;key=kk;
	}
};
int check(int tk)//检查宝石的种类数够不够 
{
	int l=0;
	for(int i=0;i<5;i++)
		if((tk>>i)&1)//位运算   (tk>>i)&i  取出tk二进制的第i位 
			l++;
	if(l>=k)  
		return 1;
	else	
		return 0;
} 
int bfs(int x,int y)
{
	queue<node> q;
	flag[x][y][0]=1;
	node temp(x,y,0,0);
	q.push(temp);
	while(!q.empty())
	{
		temp=q.front();
		q.pop();
		for(int i=0;i<4;i++)
		{
			int tx=temp.x+tr[i][0];
			int ty=temp.y+tr[i][1];
			int tt=temp.s;
			int tk=temp.key;
			if(tx<0||tx>=r||ty<0||ty>=c)
				continue;
			if(flag[tx][ty][tk]==1)
				continue;
			if(a[tx][ty]=='#')
				continue;
			if(a[tx][ty]=='E')
			{
				if(check(tk)==1)
					return temp.s+1;
			}
			if(a[tx][ty]>='0'&&a[tx][ty]<='9')
			{
				flag[tx][ty][tk]=1;
				tk=tk|(1<<(a[tx][ty]-'0'));//tk=tk|1<<k,表示将tk的第k位赋为1 
				q.push(node(tx,ty,tt+1,tk));
			}
			if(a[tx][ty]=='.')
			{
				flag[tx][ty][tk]=1;
				q.push(node(tx,ty,tt+1,tk));
			}
			if(a[tx][ty]=='$')//传送门 
			{
				flag[tx][ty][tk]=1;
				q.push(node(tx,ty,tt+1,tk));//不传送 
				for(int j=0;j<t;j++)
				{
					tx=s[j][0];
					ty=s[j][1];
					if(flag[tx][ty][tk]==0)
					{
						flag[tx][ty][tk]=1;
						q.push(node(tx,ty,tt+1,tk));
					}		
				}
			}		
		}	
	}
	return -1;	
}

int main()
{
	int T,sx,sy;
	cin>>T;
	while(T--)
	{
		memset(flag,0,sizeof(flag));
		t=0,sum=0;
		cin>>r>>c>>k;
		for(int i=0;i<r;i++)
		{
			for(int j=0;j<c;j++)
			{
				cin>>a[i][j];
				if(a[i][j]=='S')
				{
					sx=i;
					sy=j;
					a[i][j]='.';
				}
				if(a[i][j]=='$')
				{
					s[t][0]=i;
					s[t][1]=j;
					t++;
				}
				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;
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值