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