OpenJ_Bailian 4115 鸣人找佐助(二维bfs变种+特殊判重数组)

Problem
佐助被大蛇丸诱骗走了,鸣人在多少时间内能追上他呢?

在这里插入图片描述

已知一张地图(以二维矩阵的形式表示)以及佐助和鸣人的位置。地图上的每个位置都可以走到,只不过有些位置上有大蛇丸的手下,需要先打败大蛇丸的手下才能到这些位置。鸣人有一定数量的查克拉,每一个单位的查克拉可以打败一个大蛇丸的手下。假设鸣人可以往上下左右四个方向移动,每移动一个距离需要花费1个单位时间,打败大蛇丸的手下不需要时间。如果鸣人查克拉消耗完了,则只可以走到没有大蛇丸手下的位置,不可以再移动到有大蛇丸手下的位置。佐助在此期间不移动,大蛇丸的手下也不移动。请问,鸣人要追上佐助最少需要花费多少时间?

Input
输入的第一行包含三个整数:M,N,T。代表M行N列的地图和鸣人初始的查克拉数量T
0 < M,N < 200,0 ≤ T < 10

后面是M行N列的地图,其中@代表鸣人,+代表佐助。*代表通路,#代表大蛇丸的手下。

Output
输出包含一个整数R,代表鸣人追上佐助最少需要花费的时间。如果鸣人无法追上佐助,则输出-1。

SampleInput
样例输入1
4 4 1
#@##
**##
###+


样例输入2
4 4 2
#@##
**##
###+


SampleOutput
样例输出1
6

样例输出2
4

解题思路
题意:题目描述得很清楚

思路:
一开始直接用朴素bfs和增添一个新状态(克拉数)的结构体存储节点信息,真的让人wa到怀疑人生

正确的做法应当是这样:

还是二维bfs(我看到很多人增加成了三维 其实没有必要),用一个二维数组记录每个节点剩余的max查克拉数量,如果当前查克拉数量,小于等于之前经过该点时的查克拉数量,那就不用走这一步,否则,仍然可以继续走。

在这里我要提出两个关键问题

问题1:
为什么同一点后面查克拉的数量还有可能大于前面的呢?

因为他是一个点向上下左右分别BFS搜索的,可能从左边走到某个点就要消耗1个查克拉,但是从下走到相同点的路途上并没有消耗查克拉,因此可能出现这种情况。

问题二2:
为什么后一次的查克拉如果大于前一次走到这的则可以放入队列呢?

走不同的路线,查克拉的剩余量可能是不一样的。上次走这的时候如果查克拉剩余是n,但是可能走另一条路再到这的时候是大于n的。

这时候到底要不要再把这个点放进去呢?答案是,如果上次的小于这次的剩余查克拉,则可以走,因为上次走的时候保存的信息在队列的前面,如果上次是能追到佐助的话,程序会在执行到后一次走这之前就结束了,如果没结束说明上次走这的时候,剩余的查克拉不够走到佐助,所以:后一次的查克拉如果大于前一次走到这的则可以放入队列

根据这套逻辑写的一份还算简洁的代码:

#include<bits/stdc++.h>

using namespace std;

struct node//存储节点信息
{
	int x,y,step,t;//横、纵坐标,步数,查克拉数
};
int m,n,T;
const int N = 210;
char g[N][N];
queue<node> q;
int sx,sy;
int chark[N][N];
int dx[]={1,0,-1,0},dy[]={0,1,0,-1};

int bfs()
{
	while(!q.empty())
	{
		node tmp = q.front();
		q.pop();
		for(int i=0;i<4;++i)
		{
			int a = tmp.x+dx[i],b = tmp.y+dy[i];
			int tim = tmp.step;
			int c = tmp.t;
			if(g[a][b]=='+') return tim+1;
			
			if(a<0||a>=m||b<0||b>=n||chark[a][b]>=c) continue;
			if(g[a][b]=='#')
			{
				if(c>0)
				{
					chark[a][b]=c-1;
					q.push({a,b,tim+1,c-1});
				}
				continue;
			}
			chark[a][b]=c;
			q.push({a,b,tim+1,c});
		}
	}
	return -1;
}

int main()
{
	cin>>m>>n>>T;
	memset(chark,-1,sizeof chark);

	for(int i=0;i<m;++i)
	{
		for(int j=0;j<n;++j)
		{
			cin>>g[i][j];
			if(g[i][j]=='@') 
			{
				sx = i,sy = j;
				chark[sx][sy]=T;
				q.push({sx,sy,0,T});
			}
		}
	}
	cout<<bfs()<<endl;
	return 0;
}

参考的两位博主:
zp_jit

ityanger

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值