4115:鸣人和佐助

题目

描述

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

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

输入

输入的第一行包含三个整数:M,N,T。代表M行N列的地图和鸣人初始的查克拉数量T。0 < M,N < 200,0 ≤ T < 10
后面是M行N列的地图,其中@代表鸣人,+代表佐助。*代表通路,#代表大蛇丸的手下。

输出

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

分析

这是一道迷宫题,要求最短时间,自然而然地考虑bfs。

按照题意,每一个状态需要包含的要素有:当前花费时间、鸣人所在位置及查克拉、大蛇丸手下存活状态。但是,实际上,我们在存数据时,并不需要将这些要素全部保存,而只需要保存当前花费时间、鸣人位置和查克拉即可。这是因为,由于仅移动花费时间,所以,求最短时间实际上就是求最短距离,那么,在bfs的过程中,自然需要避免重复到达同一个位置的情况,也就是说,在到达某个有大蛇丸手下的位置时,该位置必定为此种路线上第一次到达的,大蛇丸手下必定还活着,否则就违背了最短路线的要求。所以,大蛇丸手下的存活状态是可以不用记录的。

考虑使用一个结构体condition来储存某一时刻的状态,包含鸣人的位置x,y,鸣人的查克拉current_t,时间time。采用queue来进行bfs,采用int close[210][210]来储存鸣人到达某一位置的最大查克拉,若未到达则记为-1。在扩展节点的过程中,如果鸣人当前查克拉小于该位置储存的最大查克拉量,则说明该状态比之前扩展过的状态更差,不可能取得更好的结果(因为可能因为查克拉量不足而导致不能选择直线路径),不再进行扩展,否则该状态可能会好过之前扩展过的状态(查克拉量更多意味着受大蛇丸手下的影响更少),需要进行扩展。

代码不难给出:

#include<iostream>
#include<string>
#include<string.h>
#include<cmath>
#include<queue>
#include<iomanip>
using namespace std;

int m, n, t;
char board[210][210];//地图
class condition {
public:
	int x, y;//坐标
	int current_t;//查克拉量
	int time;//花费时间
	condition(int x_, int y_, int ct, int t_) :x(x_), y(y_), current_t(ct), time(t_) {};
};
queue<condition> open;
int close[210][210];
bool fail = 1;

int main() {
	memset(close, -1, sizeof(close));//初始值全为-1
	cin >> m >> n >> t;
	for (int i = 0; i < m; i++) {
		for (int j = 0; j < n; j++) {
			cin >> board[i][j];
			if (board[i][j] == '@') {
				open.push(condition(i, j, t, 0));
				close[i][j] = t;
			}
		}
	}
	while (!open.empty()) {
		condition tmp = open.front();
		open.pop();
		if (board[tmp.x][tmp.y] == '+') {//如果到达终点,输出当前时间,跳出循环
			cout << tmp.time << endl;
			fail = false;
			break;
		}
		if (tmp.x > 0 && tmp.current_t > close[tmp.x - 1][tmp.y]) {//将当前节点向上下左右四个方向扩展;如果当前节点的查克拉量大于close中储存的当前位置最大查克拉量,则说明该节点可扩展
			if (board[tmp.x - 1][tmp.y] == '#') {
				if (tmp.current_t > 0) {
					close[tmp.x - 1][tmp.y] = tmp.current_t;
					open.push(condition(tmp.x - 1, tmp.y, tmp.current_t - 1, tmp.time + 1));
				}
			}
			else {
				close[tmp.x - 1][tmp.y] = tmp.current_t;
				open.push(condition(tmp.x - 1, tmp.y, tmp.current_t, tmp.time + 1));
			}
		}
		if (tmp.y > 0 && tmp.current_t > close[tmp.x][tmp.y - 1]) {
			if (board[tmp.x][tmp.y - 1] == '#') {
				if (tmp.current_t > 0) {
					close[tmp.x][tmp.y - 1] = tmp.current_t;
					open.push(condition(tmp.x, tmp.y - 1, tmp.current_t - 1, tmp.time + 1));
				}
			}
			else {
				close[tmp.x][tmp.y - 1] = tmp.current_t;
				open.push(condition(tmp.x, tmp.y - 1, tmp.current_t, tmp.time + 1));
			}
		}
		if (tmp.x < m - 1 && tmp.current_t > close[tmp.x + 1][tmp.y]) {
			if (board[tmp.x + 1][tmp.y] == '#') {
				if (tmp.current_t > 0) {
					close[tmp.x + 1][tmp.y] = tmp.current_t;
					open.push(condition(tmp.x + 1, tmp.y, tmp.current_t - 1, tmp.time + 1));
				}
			}
			else {
				close[tmp.x + 1][tmp.y] = tmp.current_t;
				open.push(condition(tmp.x + 1, tmp.y, tmp.current_t, tmp.time + 1));
			}
		}
		if (tmp.y < n - 1 && tmp.current_t > close[tmp.x][tmp.y + 1]) {
			if (board[tmp.x][tmp.y + 1] == '#') {
				if (tmp.current_t > 0) {
					close[tmp.x][tmp.y + 1] = tmp.current_t;
					open.push(condition(tmp.x, tmp.y + 1, tmp.current_t - 1, tmp.time + 1));
				}
			}
			else {
				close[tmp.x][tmp.y + 1] = tmp.current_t;
				open.push(condition(tmp.x, tmp.y + 1, tmp.current_t, tmp.time + 1));
			}
		}
	}
	if (fail) {//如果结束循环后仍然没有到达终点,则鸣人无法追上佐助
		cout << -1 << endl;
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值