题目
描述
佐助被大蛇丸诱骗走了,鸣人在多少时间内能追上他呢?
已知一张地图(以二维矩阵的形式表示)以及佐助和鸣人的位置。地图上的每个位置都可以走到,只不过有些位置上有大蛇丸的手下,需要先打败大蛇丸的手下才能到这些位置。鸣人有一定数量的查克拉,每一个单位的查克拉可以打败一个大蛇丸的手下。假设鸣人可以往上下左右四个方向移动,每移动一个距离需要花费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;
}