深度优先搜索之水洼的数量

1. 问题描述:

水洼数目有一个大小为N * M的院子,雨后积起了水,
八连通的积水被认为是连在一起的,请求出园子里面总共有多少水洼(八连通指的是下图中相对w大的*部分)
***
*w*
***
限制条件
N, M <=100
样例:
输入
N = 10, M = 12

园子如下图('W'表示积水,'.'表示没有积水)

输出3

2. 问题是要我们求解出连着的一片的水洼的数量,对于这类经典的问题,使用其它迭代等的方法是难以求解的,因为我们不知道连着的积水的区域有多少,对于这类问题的求解,我们是采用常用的无死角搜索的深度优先搜索dfs算法来解决,因为dfs能够帮助我们搜索出所有的可能,尝试去走每一条路线,直到所有的路线都被走完了,那么dfs久终止了

基于上面的分析,我们知道要使用dfs来求解,但是我们具体怎么样做呢,这里假如从数组的起始位置开始上往下搜索,那么上一个状从该数组的这个位置的八个方向开始搜索,上一个状态结束之后,那么进入到下一个状态,但是进入到下一个状态的时候它也会往自己的八个方向开始搜索,下一个状态又会搜索至上一个状态的地方,而上一个状态又会往下一个状态搜索,这就造成了递归无法出去了,永远得不到答案而且会导致栈溢出,所以我们该如何避免这种情况呢,其中比较有技巧的方法是当发现这个位置有积水之后把这个位置变为干燥,即将字符'W'变为'.',这样转移到下一个状态的时候那么往八个方向搜索的时候就不会走之前有积水的地方,因为之前有积水的地方已经干燥了,这样问题就可以解决了,当一个dfs搜索完之后那么它周围的积水都被清除掉了,那么继续寻找下一个有积水的地方然后进行dfs,当所有的积水区域都被赶走之后那么水洼的数量就计算出来了

其中涉及到搜索以自己为中心的八个方向的搜索,所以存在着八个平行状态的搜索,这里使用到了一个技巧就是使用两层的for循环来进行处理

总结一下:比较核心的问题是如何避免在下一个状态进入到上一个状态的时候进入循环的递归,那么这个时候就要将上一次的结果清除掉,这样就不会陷入到无限递归的情况

3. 具体的代码如下:

import java.util.Scanner;
public class Main {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int m = sc.nextInt();
		int n = sc.nextInt();
		int count = 0;
		char arr[][] = new char[m][n];
		for(int i = 0; i < m; i++){
			arr[i] = sc.next().toCharArray();
		}
		for(int i = 0; i < m; i++){
			for(int j = 0; j < n; j++){
				//有'W'的地方说明有积水
				if(arr[i][j] == 'W'){
					dfs(arr, i, j);
//					System.out.println(i + " " + j);
					count++;
				}
			}
		}
		System.out.println(count);
		sc.close();
	}

	private static void dfs(char[][] arr, int i, int j) {
		arr[i][j] = '.';
		//搜索八个平行状态然后看一下连通的水洼
		//因为涉及到八个平行状态那么可以使用for循环,但是我们可以使用嵌套两个for循环的技巧来进行处理
		//因为加的都是-1 0 1 而1而且左上角到右下角
		//行的长度: arr.length 列的长度: arr[0].length
		for(int k = -1; k <= 1; k++){
			for(int l = -1; l <= 1; l++){
				if(k == 0 && l == 0) continue;
				if(i + k >=0 && j + l<= arr[0].length - 1 && i + k <= arr.length - 1 && j + l >=0){
					if(arr[i + k][j + l] == 'W'){
						//继续搜寻它的下一个积水
						dfs(arr, i + k, j + l);
					}
				}
			}
		}	
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值