POJ 程序设计与算法(二)第09周测验(2020春季) 001:鸣人和佐助【DFS】【剪枝】

文章目录

    • 描述
    • 思路
    • 代码

描述

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

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

佐助在此期间不移动,大蛇丸的手下也不移动。请问,鸣人要追上佐助最少需要花费多少时间

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

样例输入

样例输入1

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

样例输入2

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

样例输出

样例输出1

6

样例输出2

4

题意:搜索一张图,其中鸣人、佐助位置已知,鸣人要找到佐助,可以上下左右移动,移动一格需要 1 1 1 单位时间,打败敌人需要 1 1 1 单位的能量。问:找到佐助的最短时间是多少?

思路

由于有着能量和敌人的存在,最短路径不一定能够走通,所以我们需要在考虑可能的更长的路。这个倒是没什么,也不是本题的难点。用DFS就好了,遍历可达的每一条路径,直到找到或者不能抵达佐助的位置。

问题在于,地图太大,搜索树每个结点最大可能扩展 3 3 3 个结点,一旦层数深了一点,要扩展的结点可能是天文数字。但是题目要求在 1 s 1s 1s 的时间内得到答案,因此我们需要好好考虑剪枝的问题。

  • (tor >= 0 && tor < m && toc >= 0 && toc < n && !vis[tor][toc]):可行性剪枝,只有地图内且没有被访问过的位置才会去访问;
  • newEnergy < 0:可行性剪枝,如果能量不够,就不会访问下一个位置;
  • totalTime + 1 >= R:最优化剪枝,要访问下一个结点时发现时间已经大于等于已知的最少时间,就不会访问;
  • totalTime + 1 >= record[tor][toc][newEnergy]:仅仅只有上述的剪枝还不够,如果我们发现 在同样的查克拉状况下来到了同样的地点但是用的时间更长 ,说明走了一条远路,不再访问。

这样就可以通过了。

代码

#include 
#include 
#include 
#include  
using namespace std;
const int MAXN = 210;
int Moves[][2] = {{-1, 0}, {0, -1}, {1, 0}, {0, 1}};
char Map[MAXN][MAXN];
bool vis[MAXN][MAXN], success;
int n, m, t, R = MAXN * MAXN, totalTime = 0;
int record[MAXN][MAXN][12]; //record[i][j][k]表示在查克拉还有k的时候访问到地图[i][j]位置时用的时间 
pair<int, int> mingren; //难得用拼音

void dfs(pair<int, int> s, int energy) { 
	if (Map[s.first][s.second] == '+') { 
		R = min(R, totalTime); //通过剪枝到达的totalTime一定会让R减少 
		success = true;
		return;
	}
	for (int i = 0; i < 4; ++i) {
		int tor = s.first + Moves[i][0], toc = s.second + Moves[i][1]; //移动到的下一个位置 
		if (tor >= 0 && tor < m && toc >= 0 && toc < n && !vis[tor][toc]) {
			char ch = Map[tor][toc]; 
			int newEnergy = (ch == '#' ? energy - 1 : energy); 
			if (newEnergy < 0 || //没有查克拉到达下一个地点 
				totalTime + 1 >= R || //如果到达下一个地点的时间超过了R, 剪枝 
				totalTime + 1 >= record[tor][toc][newEnergy])  
				continue; //如果在同样的查克拉状况下来到了同样的地点但是用的时间更长, 剪枝 
			vis[tor][toc] = true;
			record[tor][toc][newEnergy] = ++totalTime; 
			dfs(make_pair(tor, toc), newEnergy); //新的能量状况下进入下一个地点 
			--totalTime;
			vis[tor][toc] = false;  
		}
	}
}

int main() { 
	scanf("%d%d%d", &m, &n, &t);
	for (int i = 0; i < m; ++i) {
		for (int j = 0; j < n; ++j) {
			cin >> Map[i][j];
			if (Map[i][j] == '@') 
			    mingren.first = i, mingren.second = j; //鸣人行列 
		}
	} 
	R = 1 << 30, success = false;
	memset(vis, 0, sizeof(vis));
	for (int i = 0; i < MAXN; ++i) 
		for (int j = 0; j < MAXN; ++j) 
			for (int k = 0; k < 12; ++k)
				record[i][j][k] = 1 << 30;
				 
	vis[mingren.first][mingren.second] = true;
	dfs(mingren, t); //鸣人的位置和查克拉量
	if (success) printf("%d\n", R);
	else printf("-1\n"); 
	return 0; 
} 		

这几周高级会计学到了合并财务报表,要爆炸了…没时间写题目,好烦。

你可能感兴趣的:(POJ,#,BFS/DFS,===Online,Judge题记==)