四连通检测

1. 问题描述:

八连通检测-dfs的典型应用
给定一个方阵,定义连通:上下左右相邻,并且值相同
可以想象成一张地图,不同的区域被涂以不同颜色
输入:
整数N, (N<50)表示矩阵的行列数
接下来N行,每行N个字符,代表方阵中的元素
接下来一个整数M,(M<1000)表示询问数
接下来M行,每行代表一个询问,
格式为4个整数,y1,x1,y2,x2,
表示询问(第y1行,第x1列) 与 (第y2行,第x2列) 是否连通
连通输出true,否则false

例如:
10
0010000000
0011100000
0000111110
0001100010
1111010010
0000010010
0000010011
0111111000
0000010000
0000000000
3
0 0 9 9
0 2 6 8
4 4 4 6

程序应该输出:
false
true
true

2. 思路分析: 

看到题目理解之后我们可以知道需要使用到深搜来进行解决,因为深搜会搜索每一条可能的路径,尝试每一种的可能性,确定了深搜之后需要注意一下几个方面

① 因为字符存储在一个二维矩阵中,而二维矩阵表示的是一个无向的图,图与树的深搜不同的是树是没有圈的,在递归到下一层的时候不会又会到上一层进行同样的递归,而图与树不一样,它在递归的过程中搜索到下一层的时候会回到上一层又进行相同的递归那么这样就会在递归中一直出不去造成死循环,所以在这里进行深搜需要加上当前节点是否被访问的标志当进入下一次的递归的时候如果上一次的节点被标记过那么就不会再回来进行相同的递归了

像这道题目一个节点x可以往左、右、上、下的方向走,往上走节点y又进行相同的递归也是往四个方向走,如果在之前的节点增加了已经被访问过的标志那么节点y就不会往下走到x这个位置,这样就不会造成死递归了

② 需要确定深搜的方法的参数,我们知道参数是需要一直变化的,这里变化的是横坐标和纵坐标,所以在方法中需要传入检测两个点是否连通的坐标

我们可以往四个方向走,所以需要进行四次的递归,分别对应横纵坐标的变化,每一个点都可以往四个方向移动

③ 递归的一个重要的点是出口的设计,我们知道当起始点走的过程中假如是连通的那么最终一定两者的横纵坐标是相等的,所以出口是两个点的坐标相等,还有就是if条件的判断也可以限制递归的调用

④ 当退回到当前状态的时候需要进行回溯,因为我们在访问之后有可能当前的节点走下去是不符合的,所以需要把之前访问过的痕迹清除掉以便在下次尝试其它路径的时候能够访问到当前的节点

⑤ 还有需要注意的是当走到下一个点的时候需要注意下一个点的字符与上一个字符是相等的,这里可以在if条件里面进行判定是否进行递归

3. 代码如下:

import java.util.Scanner;
public class Main {
	static int visit[][];
	static char arr[][];
	static boolean flag = false;
	static int n;
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		n = sc.nextInt();
		arr = new char[n][n];
		visit = new int[n][n];
		for(int i = 0; i < n; i++){
			for(int j = 0; j < n; j++){
				visit[i][j] = 0;
			}
		}
		for(int i = 0; i < n; i++){
			arr[i] = sc.next().toCharArray();
		}
		int times = sc.nextInt();
		for(int i = 0; i < times; i++){
			int x1 = sc.nextInt();
			int y1 = sc.nextInt();
			int x2 = sc.nextInt();
			int y2 = sc.nextInt();
			visit[x1][y1] = 1;
			dfs(x1, y1, x2, y2);
			visit[x1][y1] = 0;
			System.out.println(flag);
			flag = false;
		}
		sc.close();
	}
	
	//四个方向递归走
	//if条件就是出口条件
	private static void dfs(int x1, int y1, int x2, int y2) {
		/*System.out.println(x1 + "->" + y1 + " ");*/
		//两者坐标相同说明第一个坐标已经到达了第二个坐标
		if(x1 == x2 && y1 == y2)  {
			flag = true;
			return;
		}
		//向左边走
		if(x1 - 1 >= 0 && arr[x1 - 1][y1] == arr[x1][y1] && visit[x1 - 1][y1] != 1){
			//标记该坐标已经访问过
			visit[x1 - 1][y1] = 1;
			dfs(x1 - 1, y1, x2, y2);
			//回溯
			visit[x1 - 1][y1] = 0;
		}
		
		//向右走
		if(x1 + 1 < n && arr[x1 + 1][y1] == arr[x1][y1] && visit[x1 + 1][y1] != 1){
			//标记该坐标已经访问过
			visit[x1 + 1][y1] = 1;
			dfs(x1 + 1, y1, x2, y2);
			visit[x1 + 1][y1] = 0;
		}
		
		//向上走
		if(y1 - 1 >= 0 && arr[x1][y1 - 1] == arr[x1][y1] && visit[x1][y1 - 1] != 1){
			//标记该坐标已经访问过
			visit[x1][y1 - 1] = 1;
			dfs(x1, y1 - 1, x2, y2);
			visit[x1][y1 - 1] = 0;
		}
		
		//向下走
		if(y1 + 1 < n && arr[x1][y1 + 1] == arr[x1][y1] && visit[x1][y1 + 1] != 1){
			//标记该坐标已经访问过
			visit[x1][y1 + 1] = 1;
			dfs(x1, y1 + 1, x2, y2);
			visit[x1][y1 + 1] = 0;
		}
	}
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值