程序设计与算法MOOC021:鸣人与佐助(C++DFS、剪枝)

题目要求

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

输入

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

样例输入1
4 4 1
#@##
**##
###+
****

样例输入2
4 4 2
#@##
**##
###+
****

输出

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

样例输出1
6

样例输出2
4

题目分析

这道题在DFS时把查克拉T作为一个参数会更加方便,然后把#和*分开来判断,遍历上下左右四种走法,DFS走完后记得回溯,也就是把访问置为未访问、加上的时间减回去。

此外,这道题需要剪枝操作,否则会超时。跟老师讲的一样,使用两个最优性剪枝:
1.走到这一步的时间已经超过了目前记录的最短时间,自然也就没必要走下去了,return。
2.记录走到每个点时,花费的不同查克拉情况下的时间。如果花费查克拉相等时,现在比以前花的时间长,那也没必要走下去了,return。

记得在DFS里包含起点@的情况,否则无法进入递归循环。

代码

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int M, N, T;
int flag = 0; //标记是否走到了终点
int mintime;
int totaltime;
char all[210][210];     //原始数组
int visited[210][210];  //标记是否访问过
int minl[210][210][11]; //minl[m][n][t] 表示走到m,n这个点时还有t查克拉情况下的最小时间
void dfs(int m, int n, int t)
{
    
    
	if (all[m][n] == '+') {
    
    
		mintime = min(mintime, totaltime);
		flag = 1;
		return;
	}

	if (visited[m][n] || m < 0 || n < 0 || m >= M || n >= N)
		return;
	else if (!visited[m][n] && all[m][n] == '#' && t > 0) {
    
    
		if (totaltime+1 >= mintime)
			return;
		if (totaltime+1 >= minl[m][n][t])
			return;
		++totaltime;
		minl[m][n][t] = totaltime;
		visited[m][n] = 1;
		dfs(m - 1, n, t - 1);
		dfs(m + 1, n, t - 1);
		dfs(m, n - 1, t - 1);
		dfs(m, n + 1, t - 1);
		--totaltime;
		visited[m][n] = 0;
	}
	else if (!visited[m][n] && all[m][n] == '*' || all[m][n]=='@') {
    
    
		if (totaltime+1 >= mintime)
			return;
		if (totaltime+1 >= minl[m][n][t])
			return;
		++totaltime;
		minl[m][n][t] = totaltime;
		visited[m][n] = 1;
		dfs(m - 1, n, t);
		dfs(m + 1, n, t);
		dfs(m, n - 1, t);
		dfs(m, n + 1, t);
		--totaltime;
		visited[m][n] = 0;
	}
}
int main()
{
    
    
	while (cin >> M >> N >> T) 
	{
    
    
		int mi, ni; //记录起点位置
		for (int i = 0; i < M; i++) {
    
    
			for (int j = 0; j < N; j++) {
    
    
				cin >> all[i][j];
				if (all[i][j] == '@') {
    
    
					mi = i; ni = j;
				}
			}
		}
		totaltime = 0;
		mintime = 0x3f3f3f3f;
		memset(visited, 0, sizeof(visited));
		memset(minl, 0x3f3f3f3f, sizeof(minl));
		dfs(mi, ni, T);
		if (flag == 1)
			cout << mintime;
		else
			cout << -1;
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43390123/article/details/117790819
今日推荐