NOI 6044:鸣人和佐助 BFS广度优先搜索变式/剪枝

题目

原站链接

描述

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

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

输入
输入的第一行包含三个整数:M,N,T。代表M行N列的地图和鸣人初始的查克拉数量T。0 < M,N < 200,0 ≤ T < 10
后面是M行N列的地图,其中@代表鸣人,+代表佐助。*代表通路,#代表大蛇丸的手下。
输出
输出包含一个整数R,代表鸣人追上佐助最少需要花费的时间。如果鸣人无法追上佐助,则输出-1。

样例输入

样例输入1

4 4 1
#@##
**##
###+
****

样例输入2

#@##
**##
###+
****

样例输出

样例输出1

6

样例输出2

4

题解

参考博文
这道题用BFS来做。
1、由于每一步加的时间是一样的,所以最先遇到佐助的路径一定是最优路径,所以遇到佐助就要停下来,就出答案了。
2、查克拉问题,可能最短路会由于全是守卫,所以查克拉不够用,必须要绕路才能走过去,所以不能按照普通的BFS一样用一个visit数组,这道题里面的每个节点的数值可能是要更新的。
3、肯定要有个东西作为不能继续进行下去的剪枝,不然会一直更新原来的节点,程序必然会超时。
所以,用一个Maxk数组来剪枝,Maxk数组存储如果要到这个节点所剩查克拉的最多是多少,如果后面又扩展出这个节点,那时间一定会大于等于上次扩展出这个节点。这时候,如果此时的所剩查克拉数还少于上一次所剩,那么肯定不是最优解,剪枝。

#include<bits/stdc++.h>
using namespace std;
/*位置结构体*/
struct Pos{
	int m,n; //坐标
	int t;   //在当前位置 鸣人剩余的查克拉量
	int cost;//在当前位置 累计的时间花费
	Pos(int mm,int nn, int tt, int cc){
		m = mm;n = nn;t = tt;cost = cc;
	}
}; 
/*全局变量*/
int const INF = 600000;
char Map[205][205]; //地图
int M,N,T;
int MaxT[205][205]; //走到这一步,鸣人剩余查克拉的最大值 
int ans = INF;  	//最后的输出
queue<Pos> q;
int Dir[4][2] = {0,-1,  
				0,1,
				-1,0,
				1,0}; //4个方向
/*bfs函数*/
void bfs(){
	while(!q.empty()){
		Pos cPos = q.front(); q.pop();
		if(Map[cPos.m][cPos.n]=='+'){//佐助!!!
			ans = cPos.cost;
			return; 
		}
		for(int i = 0;i<4;++i){
			int nextM = cPos.m+Dir[i][0];
			int nextN = cPos.n+Dir[i][1];
			//确保下一步合法
			if(nextM<1 || nextM > M || nextN<1||nextN>N) continue;
			//走下一步后的查克拉量
			int nextT = Map[nextM][nextN]=='#'?cPos.t-1:cPos.t; 
			if(nextT >=0 && MaxT[nextM][nextN] < nextT){
				MaxT[nextM][nextN] = nextT;  //更新MaxT
				q.push(Pos(nextM,nextN,nextT,cPos.cost+1));
			}
				
		}
	}
} 

int main(){
	cin>>M>>N>>T;
	for(int i = 1;i<=M;++i){
		for(int j = 1;j<=N;++j){
			cin>>Map[i][j];
			MaxT[i][j] = -1;
			if(Map[i][j]=='@'){
				MaxT[i][j] = T;
				q.push(Pos(i,j,T,0));
			} 
		}
	}
	bfs();
	cout<<(ans==INF?-1:ans)<<endl;
}
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值