深度优先搜索(一)—— 数独、部分和、八连通水洼

本文介绍了深度优先搜索(DFS)的基本概念,并通过数独、部分和问题及两种八连通水洼的解法进行实例解析。在数独问题中,利用DFS进行状态转移;部分和问题通过DFS和DP解决;八连通水洼解法一是以水洼为单位替换,解法二是通过状态转换避免重复计算。文章最后对DFS的应用进行了小结。

dfs基本概念

what ?: 深搜是从某一状态开始,不断的转移状态直到无法转移,然后回退到前一个状态,继续转移到其他状态,如此不断的重复,直到得到最终解; 一般dfs与递归结合解题。
回溯:递归调用代表开启一个分支,如果希望这个分支返回后某些数据恢复到分支前的状态以便重新开始,就要用回溯技巧。
剪枝:dfs时,如果已明确从当前状态无论如何转移都不会存在(更优)解,就应该中断往下继续搜索,称为剪枝。

例子

数独

import java.util.Scanner;
public class Main {
		public static void main(String[] args) {
		  char [][]table = new char[9][];
		   int x=0,y=0;
		   Scanner scanner = new Scanner(System.in);
		   for(int i=0;i<9;i++)
		      table[i] = scanner.nextLine().toCharArray();
           dfs(table,x,y);
       }

		private static void dfs(char[][] table, int x, int y) {
			if(x == 9){
				print(table);
				System.exit(0);   //退出
			}		
			if(table[x][y] == '0'){
				for(int i = 1;i < 10;i++){
					boolean res = check(table,x,y,i);
					if(res == true){
						table[x][y] = (char) ('0'+ i);
					    dfs(table,x+(y+1)/9,(y+1)%9); //转移状态
					}
				}
				table[x][y] = '0';//回溯
			}
			else{
				dfs(table,x+(y+1)/9,(y+1)%9);
			}		
		}

		private static void print(char[][] table) {
			for(int i=0;i<9;i++){
				for(int j=0;j<9;j++){
					System.out.print(table[i][j]);
				}
				System.out.println();
			}			
		}

		private static boolean check(char[][] table, int x, int y, int i) {
            for(int k=0;k<9;k++){
            	if(table[x][k] == (char)('0' + i))  return false;
                if(table[k][y] == (char)('0' + i))  return false;
            }
            for(int k=(x/3)*3;k<(x/3+1)*3;k++){
            	 for(int m=(y/3)*3;m<(y/3+1)*3;m++){
            		 if(table[k][m] == (char)('0' + i))  return false;		 
            	 }
            }
			return true;
		}	
}

在这里插入图片描述

例:输入
在这里插入图片描述
输出
在这里插入图片描述

部分和问题

可以用求解非空子集(二进制迭代)的方法解(是解此问题的最优方法).
这里就用dfs和dp分别实现。
在这里插入图片描述在这里插入图片描述

dfs
思路
在这里插入图片描述

import java.util.ArrayList;
import java.util.Scanner;
public class Main {
	public static void main(String[] args) {		     
              Scanner scanner = new Scanner(System.in);
              int n = scanner.nextInt();
              int []arr = new int[n];
              for(int i =0;i<n;i++)
            	  arr[i] = scanner.nextInt();
              int k = scanner.nextInt();
              int kk = k;
              dfs(arr,k,0,new ArrayList<Integer>(),kk);
 //new ArrayList<Integer>()相当于ArrayList<Integer> sn = new ArrayList<>();
	}

	private static void dfs(int[] arr, int k, int cur, ArrayList<Integer> s, int kk) {
		if(k==0){
			System.out.print ("Yes("+kk+"=");
			for(int i = 0;i<s.size();i++){
			  System.out.print(s.get(i));
			  if(i < s.size()-1)
			   System.out.print("+");
			}
			System.out.print(")");
			System.exit(0); //当只需一个解时   
			//当需要显示它所有可能解时,就要使用return      
		}
		if(arr.length == cur || k<0)  return;  
		dfs(arr,k,cur+1,s,kk);
		
		s.add(arr[cur]);
		int index = s.size()-1;
		dfs(arr,k-arr[cur],cur+1,s,kk);  //要arr[]这个值
		s.remove(index);  //回溯
	}
}

dp
在这里插入图片描述

//部分和
public class dp {
    static int[] arr = {3,4,5,6,9};
    static int s = 8;
    static boolean [][]table = new boolean[5][s+1];
	public static void main(String []args){
		fun(s);
		System.out.print(table[arr.length-1][s]);	
 }

	private static void fun(int s) {
		   
		for(int i1=0;i1<arr.length;i1++)
		   table[i1][0] = true;
		
		for(int i1=1;i1<=s;i1++)
			   table[0][i1] = false;		
		
		if(arr[0]<=s)
			table[0][arr[0]] = true;
		
		boolean A=false,B=false;
		for(int i=1;i<arr.length;i++){
			for(int j=1;j<=s;j++){
				if(arr[i]>j)  table[i][j] = table[i-1][j];
				else{
					A = table[i-1][j];
					B = table[i-1][j-arr[i]];
					table[i][j] = A||B;
				}
			}
		}		 
	}
}

八连通水洼 解法一

八连通积水为一个水洼
在这里插入图片描述在这里插入图片描述
思路
从任意的‘w’开始,不断的把‘w’替换为’.’(以一个水洼为单位),1次dfs就把与’w’连接的部分都替换为了‘.’,直到园子里没有‘w’.那么总共的dfs次数就是水洼个数。

import java.util.Scanner;
public class Main {
	public static void main(String[] args) {
              int res=0;
		      char arr[][] = new char[10][];
              Scanner scanner = new Scanner(System.in);
              for(int i=0;i<10;i++){
            	  arr[i] = scanner.nextLine().toCharArray();
              }
               for(int i=0;i<10;i++){
            	   for(int j=0;j<12;j++){
            		   if(arr[i][j] == 'w'){
            			   dfs(i,j,arr);
            		        res++;
            		   }
            	   }
               }
               System.out.print(res);
      	}

	private static void dfs(int i, int j,char [][]arr) {
		arr[i][j] = '.';
		for(int dx=-1;dx<=1;dx++){
			for(int dy=-1;dy<=1;dy++){
				int nx = i+dx,ny=j+dy;
				if(nx < 10 && ny <12 && nx>= 0 && ny>=0 && arr[nx][ny] == 'w')
						dfs(nx,ny,arr);
			}
		}
	}
}

八连通水洼 解法二

import java.util.Scanner;
public class Main{
	static int cnt;
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		char [][]graph = new char[10][12];
		for(int i=0;i<10;i++)
		    graph[i] = sc.nextLine().toCharArray();
	    
		for(int i =0;i<10;i++){
			for(int j=0;j<12;j++){
				if(graph[i][j] == 'w'){
					dfs(graph,i,j);
					cnt++;
				}
			}
		}
		System.out.print(cnt);
		
	}
	private static void dfs(char[][] graph, int i, int j) {
		if(i<0 || i>=10 || j<0  || j>=12)  return;
		
		//当不是w
		if(graph[i][j] == '.')  return;
		
		graph[i][j] = '.';
		
		//八个方向去探测
		dfs(graph,i,j-1);
		dfs(graph,i,j+1);
		dfs(graph,i-1,j-1);
		dfs(graph,i-1,j+1);
		dfs(graph,i+1,j+1);
		dfs(graph,i+1,j-1);
		dfs(graph,i+1,j);
		dfs(graph,i-1,j);
	}
}

其实 八连通水洼这个题中 将 w 直接替换成 . 等同于 创建一个标记数组解决。

问题

部分和问题 使用求非空子集的方法 实现,后面补充。

小结

数独部分和用到了回溯,水洼 存在状态互相转移的问题,巧妙的将 w 换成 . 就可以避免。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值