hdu1728(dfs深度查找解决迷宫问题,包含点剪枝和回溯的知识)

题目链接http://acm.hdu.edu.cn/showproblem.php?pid=1728
题目大意:
判断两个点,是否在指定弯数内的路径,如果有就输出yes,否则就输出no
解题思路
首先就是进行迷宫的地图dp(见代码及注释)
转弯的判断代码:a != -1 && i != a
还有就是要对相对应的路径进行剪枝,不然会超时。
下面是剪枝的说明(只是举得特例,便于理解):

在这里插入图片描述
假设有这么一题,给予上面的地图,让你在最大的转弯数k <= 2的情况下,判断从是否有路径从起点到终点。
在这里插入图片描述
上图中的红色箭头标的这一条路径时我们所能找到的一条路径,其中的橙色数字是turn数组里的值.
在这里插入图片描述
上图中的绿色箭头的另一条路径,但是我们可以看见路径是不符合要求的。
这个其实就是代码中的"turn[new_x][new_y]<turn[x][y]"情况,
即我们顺着绿色箭头看,从(1,1)一直到(3,3),而当到(3,3)时我们可以注意到,这时的turn[new_x][new_y]即为turn[3][2]是因为之前那条路径所经过了的,故为1;
而turn[x][y]为上一步即为turn[3][3]=2;
1<2,所以:turn[new_x][new_y]<turn[x][y]就可以判断这个路径时不符合要求的。

而为什么不能加上turn[new_x][new_y]==turn[x][y]的条件呢?下图就给你答案
在这里插入图片描述
我们可以看到,绿色箭头指的这条路径时可以到达终点的,而在(2,2)点正好是符合turn[new_x][new_y]==turn[x][y]这个要求的。
如果我们加上turn[new_x][new_y]==turn[x][y]的条件就判断为不符合要求的路径的话,会导致误杀掉类似的这种路径,所以不能加这个条件。
在这里插入图片描述
上面这图则是证明了符合a != -1 && i != a && turn[new_x][new_y] < turn[x][y]+1条件可以判断为不符合条件的路径
a != -1 && i != a 就是转弯,这个正好是上一步turn[x][y]与新当前步turn[new_x][new_y]处于转弯处
turn[new_x][new_y] < turn[x][y]+1与turn[new_x][new_y]<turn[x][y]的思想类似,无非一个在转弯处,一个不再转弯处

ac代码:

#include<stdio.h>
#include<string.h>


#define N 105


int graph[N][N];//用来记录地图的 
int turn[N][N];//用来记录每个位置之前的转弯次数 
int flag;//标记是否已经达到终点  
int goal_x,goal_y;//记录目标点位置
int mx[4]={0,1,0,-1};//用来移动标记点的 左下右上的顺序 
int my[4]={-1,0,1,0};
int column;
int row;

int k;//最多转弯次数 

void dfs(int x,int y,int a){//a用于传递判断是否有转弯
									//turn用于记录转弯次数
	int new_x,new_y;
	
	if(x == goal_x && y == goal_y && turn[x][y] <= k){//到达终点且没有大于晕数,算是成功
		

		
		flag = 1;
		
		printf("yes\n");
		
		return;	
	}
	
	if(flag == 1){//证明已经可以成功出去了 
		return;
	}
	
	if(x!=goal_x && y!=goal_y && turn[x][y] == k){
		//如果一个坐标与终点目标的x和y都不相同且转弯次数已经达到了最大限度
		//可以直接return掉。因为这样的坐标到终点至少要再转弯1一次 
		//即超出了限度,就没有必要再查找下去了 
		return;
	}
	
	if(turn[x][y] > k){//超出限度,return结束掉 
		return;
	}
	

	
	for(int i = 0;i < 4;i++){

		new_x = x + mx[i];
		new_y = y + my[i];
		
		
		//下面两个if即为剪枝操作 *******
		
		//turn[new_x][new_y]为新当前步的转弯数 turn[x][y]为上一步转弯数 
		if(turn[new_x][new_y] < turn[x][y]){//turn[new_x][new_y]初始设为了0x3f
											//故如果小于上一步的转弯数则证明已经被访问过了
											//不可等于,等于的话会少算次数(上面思路上有举例子说明) 
			continue;
		}
		
		if(a != -1 && i != a && turn[new_x][new_y] < turn[x][y]+1){//这个正好是上一步turn[x][y]
																//	与新当前步turn[new_x][new_y]处于转弯处
																//其它的思路与上一个if道理一样 (思路中也有图解) 
		 	continue;
			 	
		}


		
		if(a != -1 && i != a){//当不等于起点并且查找的方向发生变化时,便视为一次转弯 
		
				turn[new_x][new_y] = turn[x][y] + 1;
				
		}else{//不是的话就将上一步的转弯值存入其中。****有点类似dp思想
		 
			turn[new_x][new_y] = turn[x][y];
			
		}
		
		
		
		if(new_x >= 1 && new_x <= row && new_y >= 1 && new_y <= column
		 && flag == 0 && graph[new_x][new_y] == 0){
			
		
				
//			printf("我进来了?\n");

			graph[new_x][new_y] = 1;
			
			dfs(new_x,new_y,i);
			
			graph[new_x][new_y] = 0;

		}
		
		
	}
}




int main(){
	int t;
	int x,y;
	char temp;
	scanf("%d",&t);

	while(t--){
		
		flag = 0; 
		memset(graph,0,sizeof(graph));
		memset(turn,0x3f,sizeof(turn));
		
		scanf("%d%d",&row,&column);
		for(int i = 1;i <= row;i++){
			getchar();//用于接收前面的回车 
			for(int j = 1;j <= column;j++){

				scanf("%c",&temp);
				if(temp == '*'){
					graph[i][j] = 1;//1为障碍点 
				}
			}
		}

		scanf("%d",&k);
		scanf("%d%d",&y,&x);
		scanf("%d%d",&goal_y,&goal_x);
		
		turn[x][y] = 0;//将起点的转弯次数设置为0 
		
//		for(int i = 1;i <= row;++i){
//			
//			for(int j = 1;j <= column;++j){
//			printf("%d",graph[i][j]);
//			}
//			printf("\n");
//		}
//		printf("--------\n");
		
		dfs(x,y,-1);
		
		if(flag == 0){
			printf("no\n");
		}
		

		
	}
	
	
	
	return 0;
	
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值