【NOI OJ】 2.5 搜索 7221 拯救公主

本文介绍了一种利用广度优先搜索(BFS)解决迷宫游戏问题的方法,特别是针对包含多种钥匙和特殊传送门的游戏场景。通过巧妙运用二进制来记录不同类型的钥匙,并处理复杂的传送逻辑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    由于一些原因,本题无法把题目描述放上来,可以去OpenJudge上看看原题……


    题目描述简直极其恶心,可以让你随时吐出来,本着大无畏的精神,我们分析一下题目:

1.S E :这两个点是迷宫问题最基本的要素,完全可以忽略不计

2.# . :这两种元素也是同上,见怪不怪了……

3.0-4 :这是本题坑点之一,每种钥匙都极其恶心,可能有相同的钥匙是恶心来源之一,最主要的是判断是否拥有目标把钥匙,恶心至极

4.$   :题目的巨坑,可以多次传送让主角可以自由穿越墙面拿取钥匙然后再传送回来,无法标记vis(因为可能会被再次使用),是比钥匙更恶心的存在

    好吧,分析完后开始想对策……

    首先,可以一眼看出使用广搜(bfs),那么接下来怎么办?钥匙怎么解决?不会用数组来标记吧?我一开始还真是弄了一个队列数组,但后来很难处理,所以不得不抛弃数组……我们可以发现:0-4可以巧用二进制来记录收集的宝石(比如:1和2两个宝石为2+2*2=6,0和1为1+2=3,0和4为1+2*2*2*2=17),可以巧妙的解决这个问题,标记数组vis开成三维即可。好吧,既然这样,我们再来思考一下如何传送。可以看出,传送门不需要时间传送,所以走到一个传送门,就相当于走到了所有传送门,那么就简单了……不过不要忘了恢复现场哦,代码如下:

#include<cstdio>
#include<queue>
#include<cmath>
#include<cstring>
using namespace std;
queue<int>X;
queue<int>Y;
queue<int>D;
queue<int>Sum;
int t,n,m,k,sx,sy,fx,fy,f;
char S[205][205];
int vis[205][205][50];
int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
bool check(int x,int y,int l)
{
	if(x<1||y<1||x>n||y>m)
	{
		return 0;
	}
	if(S[x][y]>='0'&&S[x][y]<='4')
	{
		int h=pow(2,S[x][y]-'0');
		if(!vis[x][y][l+h])
			return 1;
	}
	if(S[x][y]=='#'||vis[x][y][l])
	{
		return 0;
	}
	return 1;
}	
void bfs()
{
	while(!X.empty())
	{
		int x=X.front(),y=Y.front(),d=D.front(),sum=Sum.front(),num=0;
		if(d>=16)
		{
			num++;
		}
		if(d%16>=8)
		{
			num++;
		}
		if(d%8>=4)
		{
			num++;
		}
		if(d%4>=2)
		{
			num++;
		}
		if(d%2>=1)
		{
			num++;
		}
		for(int i=0;i<4;i++)
		{
			int xx=x+dir[i][0],yy=y+dir[i][1];
			if(check(xx,yy,d))
			{
				if(xx==fx&&yy==fy&&num>=k)
				{
					f=1;
					printf("%d\n",sum+1);
					return;
				}
				if(S[xx][yy]>='0'&&S[xx][yy]<='4')
				{
					int o=pow(2,S[xx][yy]-'0');
					if(d%(o*2)<o)
					{
						X.push(xx);
						Y.push(yy);
						D.push(d+pow(2,S[xx][yy]-'0'));
						Sum.push(sum+1);
						vis[xx][yy][d+1]=1;
						continue;
					}
				}
				if(S[xx][yy]=='$')
				{
					for(int i=1;i<=n;i++)
					{
						for(int j=1;j<=m;j++)
						{
							if((i!=xx||j!=yy)&&S[i][j]=='$'&&vis[i][j][d]==0)
							{
								vis[i][j][d]=1;
								X.push(i);
								Y.push(j);
								D.push(d);
								Sum.push(sum+1);
							}
						}
					}
				}
				X.push(xx);
				Y.push(yy);
				D.push(d);
				Sum.push(sum+1);
				vis[xx][yy][d]=1;
			}
		}
		X.pop();
		Y.pop();
		D.pop();
		Sum.pop();
	}
}
int main()
{
	scanf("%d",&t);
	for(;t;t--)
	{
		scanf("%d %d %d\n",&n,&m,&k);
		while(!X.empty())
		{
			X.pop();
			Y.pop();
			Sum.pop();
			D.pop();
		}
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=m;j++)
			{
				S[i][j]=getchar();
				if(S[i][j]=='S')
				{
					vis[i][j][0]=1;
					sx=i;
					sy=j;
					X.push(i);//纵坐标
					Y.push(j);//横坐标
					D.push(0);//收集的宝石数据化
					Sum.push(0);//行走的步数
				}
				if(S[i][j]=='E')
				{
					fx=i;
					fy=j;
				}
			}
			getchar();
		}
		bfs();
		if(!f)
		{
			printf("oop!\n");
		}
		memset(vis,0,sizeof(vis));
		memset(S,0,sizeof(S));
		f=0;
	}
}

    嗯嗯嗯……不要无视最后的文字,有什么不懂的请在下方评论问我哦~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值