(最优解法)46行代码AC_HDU1242 Rescue(DFS解法+BFS解法)

励志用少的代码做高效表达


Problem Description

Angel was caught by the MOLIGPY! He was put in prison by Moligpy. The prison is described as a N * M (N, M <= 200) matrix. There are WALLs, ROADs, and GUARDs in the prison.
Angel’s friends want to save Angel. Their task is: approach Angel. We assume that “approach Angel” is to get to the position where Angel stays. When there’s a guard in the grid, we must kill him (or her?) to move into the grid. We assume that we moving up, down, right, left takes us 1 unit time, and killing a guard takes 1 unit time, too. And we are strong enough to kill all the guards.
You have to calculate the minimal time to approach Angel. (We can move only UP, DOWN, LEFT and RIGHT, to the neighbor grid within bound, of course.)

Input

First line contains two integers stand for N and M.
Then N lines follows, every line has M characters. “.” stands for road, “a” stands for Angel, and “r” stands for each of Angel’s friend.
Process to the end of the file.

Output

For each test case, your program should output a single integer, standing for the minimal time needed. If such a number does no exist, you should output a line containing “Poor ANGEL has to stay in the prison all his life.”


题目链接——>HDU-1242


分析与思考

题意:天使被魔鬼抓走了,他的一群朋友(是一群!!!)准备救他, 有可能碰到守卫,要想通过需要步数+2。求天使与其朋友之间最短步数。

如果救不出来,则需要输出:“Poor ANGEL has to stay in the prison all his life.”

考虑到该死的天使有一群朋友, 因此我们需要将起点与终点互换,起点标记为天使的位置(因为天使只有一个),找到离他最近的朋友输出即可。

可选用的算法有DFS或BFS。 我使用的是DFS求解。 BFS解法后续更新。 请持续关注~。


二更:

更新了BFS的解法, 其中DFS解法耗时78MS, BFS解法耗时15MS, 笔者查看了网上大部分的代码,目前来讲的最优解,应该就是基于BFS的算法了。

注意:在用BFS解题时,我们往往会觉得第一个入队的’r’一定代表最短路(老惯性思维了,该死), 但本题的特殊点在于有’x’守卫,遇守卫需+2步,因此在迭代时可能得不到最优解,如这组数据:

2 5
axxxr

正确答案是:6
错误答案是:7

虽然不考虑这种特殊情况的代码,提交也可以AC。
但作为一个处女座程序猿,就想把代码搞到完美~
解决办法是:定义一个Min变量,有多条路时保存最小值即可。

网上很多的博客都忽略了这一点,大家在参考时一定要注意。 代码二展示了正确的宽搜解法。

后续更新DFS+BFS原理讲解, 请持续关注哦~


总结:本题坑点较多, 需仔细理解题意才能保证不错。


代码1_DFS解法

#include<bits/stdc++.h>
using namespace std;
char a[210][210];		//迷宫地图 
int vis[210][210];		//标记地图中某点是否走过 
int n, m;				//迷宫的行列大小 
int startx, starty;		//迷宫起点坐标
int Min = 999999999;	//所用最小步数 

int Next[4][2] = {
    
    			//方向数组 
				{
    
    1, 0},		//向上
				{
    
    -1, 0}, 	//向下 
				{
    
    0, 1},		//向右 
				{
    
    0, -1}};	//向左

void dfs(int x, int y, int step) {
    
    
	//边界条件:找到终点,更新最小值
	if(a[x][y] == 'r') {
    
     
		Min = min(step, Min);
		return;
	} 	
	
	//若不是终点,判断下一步是否为障碍物,是否越界,是否已经走过,若无,则走 
	if(a[x][y]=='.' || a[x][y]=='x' ||  a[x][y]=='a' ) {
    
     	//1、判断是否是障碍物 
		for(int i = 0; i < 4; i++) {
    
    
			 int t1 = x + Next[i][0];
			 int t2 = y + Next[i][1];
			 if(t1<0 || t1>=n || t2<0 || t2>=m) continue;	//2、是否越界
			 if(!vis[t1][t2]) {
    
    	//3、是否已经走过 
			 	vis[t1][t2] = 1;
			 	
			 	if(a[x][y]=='x') dfs(t1, t2, step+2);
			 	else dfs(t1, t2, step+1);
			 	
			 	vis[t1][t2] = 0;
			 }
		} 
	}
} 

int main() {
    
    
	ios::sync_with_stdio(false); 
	while(cin>>n>>m) {
    
    
		Min = 999999999;
		memset(vis, 0, sizeof(vis));
		for(int i = 0; i < n; i++) 
			for(int j = 0; j < m; j++) {
    
    
				cin>>a[i][j];
				if(a[i][j] == 'a') {
    
     startx=i, starty=j; }	//由于朋友有多个,因此记录天使位置,找朋友 
			}
		dfs(startx, starty, 0);
		
		if(Min == 999999999) cout << "Poor ANGEL has to stay in the prison all his life." << endl; 
		else cout << Min << endl;
	}
return 0; } 

代码2_BFS解法

#include<iostream>
#include<cstring> 
#define Max 40100 
using namespace std;

struct List{
    
    
	int x;	//横坐标
	int y;	//纵坐标
	int sum; //总步数 
};

char a[210][210];
int vis[210][210];

int Next[4][2] = {
    
    
			{
    
    1, 0},
			{
    
    -1, 0},
			{
    
    0,1},
			{
    
    0,-1}};

int main() {
    
    
	ios::sync_with_stdio(false); 
	int n, m; while(cin>>n>>m) {
    
    
		
		//初始化与定义变量 
		memset(vis, 0, sizeof(vis));	//每次循环vis数组都需初始化 
		int startx, starty;		//起点的x、y坐标
		int Min = 999999999;	//最后的输出结果 
		bool flag = false;	//终止循环的标记 

		//输入 
		for(int i = 0; i < n; i++) 
			for(int j = 0; j < m; j++) {
    
    
				cin>>a[i][j];
				if(a[i][j] == 'a') startx=i, starty=j;
			}  
			
		
		
		//队列初始化
		int head=0, tail=0;	//头结点,尾结点 
		List l[Max];
		l[tail].x = startx;
		l[tail].y = starty;
		l[tail].sum = 0;
		tail++;
		vis[startx][starty] = 1;
		flag = 0;		//标记是否到达目标点 
		
		//当目标不为空时循环 
		while(head < tail) {
    
    		//如果尾指针小于等于头指针,代表队列中无元素,返回 
			for(int i = 0; i < 4; i++) {
    
    
				int tx = l[head].x + Next[i][0];
				int ty = l[head].y + Next[i][1]; 
				//终止本次循环的三种条件: 越界、该点是障碍物、已经被搜索过。 
				//1、若越界则返回 
				if(tx<0 || tx>=n || ty<1 || ty>=m) continue;
				//2、若没被搜索过且该点不是障碍物,则: 
				if(!vis[tx][ty]) 
					if(a[tx][ty] != '#') {
    
    
						vis[tx][ty]=1;
						l[tail].x = tx;
						l[tail].y = ty;
						//如果是x,则走两步 
						if(a[tx][ty] == 'x') l[tail].sum = l[head].sum+2; 
						else l[tail].sum=l[head].sum+1;
						
	 					tail++; 	//每次循环后,tail都需自加 
					}
				if(a[tx][ty]=='r') 	//若等于r,则与Min比较,取较小值。 
					Min = min(l[tail-1].sum, Min);
			}
			head++;	//每次循环都要丢弃头部元素 
		}
		
		//输出 
		if(Min == 999999999) cout << "Poor ANGEL has to stay in the prison all his life." << endl;
		else cout << Min << endl;
	}		
	return 0;
} 

这世界就是,一些人总在昼夜不停的努力,而另外一些人,起床就发现世界已经变了。

猜你喜欢

转载自blog.csdn.net/weixin_43899069/article/details/108741159