hdu1072(可以分别应用bfs和dfs进行走迷宫)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1072
题目大意:
就是给你一个n*m的地图,地图的数字
0=墙 1=可以走的平地 2=起点 3=终点 4=炸弹时间重置点
细节性的东西:到达终点或者是炸弹时间重置点时,你限定的时间为最后一秒,则这样的路径是不成立的,即这样的路径不合题意。
bfs思路

bfs思路:就是发散。通过一个点发散到多个点,然后再由多个点再发散到另外多个点。下面的代码中有一句话,就是:map[sx][sy]=0; //这句话去掉与否都不影响答案
最开始不加上这句话是也是可以过得。我想了一下有没有可能起点会经过两次呢?就像那个测试案例三一样。

测试案例三:这个走的路径数是13为(2->1->1->1->4->1->1->1->1->4->1->1->1->3)第一个4的左右所经过的1就是同一个1.。
5 8
1 2 1 1 1 1 1 4
1 0 0 0 1 0 0 1
1 4 1 0 1 1 0 1
1 0 0 0 0 3 0 1
1 1 4 1 1 1 1 1

所以我在想有没有可能出现这种情况:(就是起点2要经过4然后再出来,就像下面的图一样。然后我发现就是2不经过4到终点3的步数是4,而2到4再出来到3的步数是5。所以基本上可以认为不存在起点经过两次的情况。所以map[sx][sy]=0; 可以设置。当然删掉这句话也是可以ac的,时间复杂度是符合要求的
3 6
0 2 1 1 1 3
0 4 0 0 0 0

bfs的ac代码:

#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;

struct node{
	int x;
	int y;
	int step;
	int time;
};

int n,m;
int dx[4]={0,1,0,-1};//左下右上 
int dy[4]={-1,0,1,0};
int map[10][10];
int sx,sy;

void bfs(){
	queue<node> Q;
	node q;
	q.x = sx;
	q.y = sy;
	q.step = 0;
	q.time = 6;
	Q.push(q);
	map[sx][sy]=0; //这句话去掉与否都不影响答案 
	while(!Q.empty()){
		node p;//用来接收前一个的数据 
		p = Q.front();
		Q.pop();
		for(int i = 0;i < 4;i++){//进行发散查找 
			node k;
			k.x = p.x+dx[i];
			k.y = p.y+dy[i];
			k.step = p.step+1;
			k.time = p.time-1;
			if(k.x<1||k.x>n||k.y<1||k.y>m||map[k.x][k.y]==0||k.time<=0){
				continue;
			}
			if(map[k.x][k.y]==3){
				cout<<k.step<<endl;
				return;
			}
			if(map[k.x][k.y]==4){//如果是碰到重置点就
								//时间重置 
				k.time = 6;
				map[k.x][k.y] = 0;
			}
			Q.push(k);
		}
		
	}
	cout<<-1<<endl;
}


int main(){
	int t;
	cin>>t;
	while(t--){
		cin>>n>>m;
		for(int i = 1;i <= n;i++){
			for(int j = 1;j <= m;j++){
				cin>>map[i][j];
				if(map[i][j] == 2){
					sx = i;
					sy = j;
				}
			}
		}
		
		bfs();
		
	}
	
	
	
	return 0;
	
}

dfs思路

说一下dfs思路吧,就是正常地左下右上地搜索。然后需要step[10][10]和time[10][10]的两个数组,分别记录每个点的走过的步数以及在这个点所剩的时间。
然后这里需要用到剪掉一些用不到的路径,剪的方法如下:
就是step数组初始值设置个无穷大的值
然后遍历的时候,当sum>=step[x][y]&&T<=time[x][y]时就将这个路径剪掉,即ruturn掉
原因是:sum代表当前点的步数,T代表当前点的所剩时间。而在step[x][y]和time[x][y]数值还没有更新的时候,sum大于step[x][y]证明,之前有路径走过这个(x,y)点【注:因为step初始值是设置成类似无穷大的值的啦】。然后既然证明有(x,y)被走过,那么也就是所time[x][y]也是代表之前那个路径走过所留下的time值。
所以当前的步数大于或者等于之前路径的步数,且时间小于等于之前路径的时间,那就不用走了。最后可定是走不到终点的。【故剪掉】

dfs的ac代码:

#include<iostream>
#include<cstdio>
#include<cstring> 
using namespace std;


int map[10][10];
int n,m;//迷宫的行和列 
int step[10][10];//记录当前之前所走的步数 
int time[10][10];//记录每步的时间 
int dx[4]={0,1,0,-1};
int dy[4]={-1,0,1,0};
int sx,sy;
int ex,ey;
int minstep;//用来记录最短的时间 
//int maxtime;//最大的限定时间 

int dfs(int x,int y,int sum,int T){
	

	
	
//	cout<<x<<"|"<<y<<"|"<<sum<<"|"<<T<<endl;
	if(x == ex && y == ey){//到达终点时 
		if(minstep == -1||minstep > sum){
			minstep = sum;
		}
	}
	
	if(T <= 0){//当时间等于maxtime的时候就返回 
		return 0;
	}
	
	if(sum >= step[x][y] && T <= time[x][y]){//当走到重复的路径时且当前的时间小于等于之前的time就直接结束 
		return 0;
	}
	
	if(map[x][y] == 4){//当遇见炸弹重置点将时间重置为6 
		T = 6;
	}
	
	step[x][y] = sum;
	time[x][y] = T;
	

	for(int i = 0;i < 4;i++){
		int xx = x + dx[i];
		int yy = y + dy[i];
		if(xx < 1||xx > n||yy < 1||yy > m||map[xx][yy] == 0||map[xx][yy]==2){
			continue;
		}
		
		if(T <= 1 && map[xx][yy] == 4){//当碰见炸弹重置点时时间为0了 
			continue;
		}
		
//		cout<<"地图位置"<<map[xx][yy]<<endl;
		if(T <= 1 && map[xx][yy] == 3){//当到达终点时,时间为0了 
//			cout<<"没进来?"<<endl;
			continue;
		}
		
		dfs(xx,yy,sum+1,T-1);
	}
	
	return minstep;
}



int main(){
	int t;
	cin>>t;
	int ans;
	while(t--){

		cin>>n>>m;
		for(int i = 1;i <= n;i++){
			for(int j = 1;j <= m;j++){
				cin>>map[i][j];
				if(map[i][j] == 2){
					sx = i;
					sy = j;
				}
				if(map[i][j] == 3){
					ex = i;
					ey = j;
				}
			}
		}
		memset(step,0x3f,sizeof(step));
//		cout<<sx<<sy<<ex<<ey<<endl;
		memset(time,0,sizeof(time));
		
		minstep = -1;
		
//		maxtime = 6; 
		ans = dfs(sx,sy,0,6);
		cout<<ans<<endl;
		
	}
	
	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值