计蒜客题解——T1214:鸣人和佐助

题目相关

题目链接

计蒜客 OJ,https://nanti.jisuanke.com/t/T1214

题目描述

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

输入格式

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

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

输出格式

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

样例输入1

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

样例输出1

6

样例输入2

4 4 2
#@##
**##
###+
****

样例输出2

4

题目分析

题意分析

一个 M*N 大小的迷宫,我们从 @ 位置出发(也就是起点),字符 * 表示可以安全通行的方格,字符 # 表示有怪物(可以杀死,但是需要付出代价),字符 * 表示仙药(也就是终点)。要求输出从 @ 到 * 的最短路径。那么迷宫问题的基本要素全齐了,所以本题就是一道 BFS 模板题。

和上一题,https://blog.csdn.net/justidle/article/details/104662118,最大的不同是在于:杀死大蛇丸的手下是需要付出代价的,也就是所谓的查克拉。而题目提供的查克拉是有限的。因此本题最大的变化就是如何表示查克拉的变化。

对于 # 表示的地点,我们有两种操作。第一种,查克拉够,我们可以杀死大蛇丸的手下,通过这个地方。第二种查克拉不够,那么意味着这里不能通过,需要绕道。如何表示查克拉,是本题最大的变化。因为某个位置导致查克拉变化后,其后所有状态都会发生改变。所以我们将 visit 数组从二维变为三维,也就是最后一维表示不同查克拉状态的访问性。

样例数据分析

如果想看类似的数据分析,可以看以前的文章,https://blog.csdn.net/justidle/article/details/104651311。主要是我偷懒了,画图太累了,请原谅。

算法思路

1、读入数据,并写入到合适的数据结构中。

2、找到起点位置,将起点加入到队列 q 中。

3、记录终点位置信息。

4、开始 BFS 遍历。直到找到终点或者遍历所有节点而无法到达终点。碰到 # 的道路,需要判断查克拉。

AC 参考代码

#include <cstdio>
#include <queue>

struct POS {
    int x, y;//坐标
    int t;//查克拉数
    int dis;//距离 
};

const int MAXN = 202;
const int MAXT = 10;
struct MAZE {
	int row, col;
	int t;
	char data[MAXN][MAXN];//迷宫数据 
	bool visit[MAXN][MAXN][MAXT];//访问性 
	int x1, y1;
	int x2, y2;
};

int bfs(MAZE &maze);

int main() {
	MAZE maze = {};//迷宫定义
	
	//读入迷宫长宽 
	scanf("%d %d %d", &maze.row, &maze.col, &maze.t);
	
	//读入迷宫数据
	int i,j;
	for (i=0; i<maze.row; i++) {
		for (j=0; j<maze.col; j++) {
			scanf(" %c", &maze.data[i][j]);
			if (maze.data[i][j]=='+') {
				//终点
				maze.x2 = i;
				maze.y2 = j; 
			} else if (maze.data[i][j]=='@') {
				//起点
				maze.x1 = i;
				maze.y1 = j; 
			}
		}
	} 
	
	int ans = bfs(maze);
	
	printf("%d\n", ans);
	
	return 0;
}

int bfs(MAZE &maze) {
	std::queue<POS> q;
	const POS move[] = {{-1,0}, {0,1}, {1,0}, {0,-1}};
	POS cur, next;
	
	//加入起点
	cur.x = maze.x1;
	cur.y = maze.y1;
	cur.dis = 0;
	cur.t = maze.t;
	q.push(cur); 

	maze.visit[cur.x][cur.y][cur.t] = true;
	
	//开始遍历
	while (!q.empty()) {
		cur = q.front();
		q.pop();
		

		for (int i=0; i<4; i++) {
			next.x = cur.x + move[i].x;
			next.y = cur.y + move[i].y;
			
			//判断是不是终点
			if (next.x==maze.x2 && next.y==maze.y2) {
				return cur.dis+1;
			} 
			
			//判断通过性
			if (next.x>=0&&next.x<maze.row&&next.y>=0&&next.y<maze.col) {
				if (maze.data[next.x][next.y]!='#') {
					//通路或者佐助 
					if (maze.visit[next.x][next.y][cur.t]==false) {
						next.t = cur.t;
						next.dis = cur.dis + 1;
						q.push(next);
						
						maze.visit[next.x][next.y][next.t] = true;
					}
				} else {
					//大蛇丸的手下
					if (cur.t > 0 && maze.visit[next.x][next.y][cur.t-1]==false) {
						next.t = cur.t - 1;
						next.dis = cur.dis + 1;
						q.push(next);
						
						maze.visit[next.x][next.y][next.t] = true;
					} 
				}
			}
		}
	}
	
	return -1; 
}

代码分析

1、由于有查克拉的存在,每个结点需要增加查克拉信息记录。代码部分如下:

struct POS {
    int x, y;//坐标
    int t;//查克拉数
    int dis;//距离 
};

2、关于 # 的处理。注意代码中对通过性的判断。以前题目中,我们是将本节点能否走和其他条件一起判断。这里,我们需要分开判断,先判断其他条件,再判断是否为大蛇丸的手下(即 #)。代码部分如下:

if (maze.data[next.x][next.y]!='#') {
    不是大蛇丸的手下,那么操作和以前一样
} else {
    大蛇丸手下
    先判断查克拉是否够用  cur.t>0
    再判断查克拉减 1 后是否访问过   maze.visit[next.x][next.y][cur.t-1]==false 
}
发布了206 篇原创文章 · 获赞 107 · 访问量 105万+

猜你喜欢

转载自blog.csdn.net/justidle/article/details/104662604