深度优先搜索入门

来源:互联网 发布:加密锁软件下载 编辑:程序博客网 时间:2024/05/21 06:56

简介:

深度优先搜索(depth-first-search)简称DFS,其特点的就是一条路走到黑,不撞南墙不回头。在算法竞赛中DFS算法应用算是最广泛。它从某个状态开始,不断地转移状态直到无法转移,然后回退到前一步的状态,继续转移到其他的状态,(可以利用这个特点进行提前返回无法搜到的路,这个也叫做剪枝,这也是避免DFS算法超时的技巧)如此不断重复,直到找到最终的解。


深度优先搜索应采用递归实现比较简单。

DFS一般框架:

void  dfs(int step){if(step == n){//判断边界return ;//返回}for(int i = 0; i<= n; i++){//尝试每一种可能dfs(step + 1); //继续下一步}}

dfs 和递归的区别是,dfs 是一种算法,注重的是思想,而递归是编程语言的一种写法。我们通过递归的写法来实现 dfs。下面我们通过一个实际问题来理解 dfs 到底干了什么。

迷宫问题

用 2 维数组来表示一个迷宫

  1. S##.
  2. . . . .
  3. ###T
‘S’表示起点,‘T’表示终点,‘#’表示墙壁,‘ . ‘表示平地。你需要从‘S’出发走到‘T’,每次只能上下左右走动,并且不能走出地图,也不能走进墙壁,每个点只能通过一次。现在要求你求出有多少种走的方案。
  我们尝试用dfs 来求解这个问题。先找到起点,每个点按照左,下,右,上的顺序尝试,从起点‘S’开始,走到下一个点以后,把这个点再当做起点‘S’继续按照顺序尝试,如果某个点上下左右都尝试走过了以后,便把起点回到走到这个点的点。继续尝试其他方向。直到所有点都尝试走了上下左右。好比你自己去走这个迷宫,你也要一个方向一个方向的尝试着走,如果这条路不行,就回头,尝试下一条路,现在由程序来完成这个过程。


废话不多说,直接上代码。

实现代码

import java.util.*;public class Main {    static String[] mat = new String[11]; // 地图信息    static int ans, n, m; // ans 用来记录最后答案 n, m 表示行列    static int dir[][] = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}};  //对应的上下左右4各方向    static boolean vis[][] = new boolean[11][11];// 用来标记是否访问    static void dfs(int x, int y) {        vis[x][y] = true;        if(mat[x].charAt(y) == 'T'){            ans ++;            return;        }        int next_x = 0;int next_y = 0;        for(int i = 0; i< dir.length; i++){            next_x = x+dir[i][0];            next_y = y+dir[i][1];            if(next_x<0||next_x>=n||next_y<0||next_y>=m) continue;            if(vis[next_x][next_y] == false&&mat[next_x].charAt(next_y)!='#'){                vis[next_x][next_y] = true;                dfs(next_x,next_y);                vis[next_x][next_y] = false;            }        }          vis[x][y] = false;    }    public static void main(String[] args) {        Scanner cin = new Scanner(System.in);        n = cin.nextInt();        m = cin.nextInt();        for (int i = 0; i < n; ++i) {            mat[i] = cin.next();        }        int x = 0, y = 0;        for (int i = 0; i < n; ++i) {            for (int j = 0; j < m; ++j) {                if (mat[i].charAt(j) == 'S') {                    x = i;                    y = j;                }            }        }        ans = 0;        dfs(x, y);        System.out.println(ans);    }}


抽象式的dfs

  前面用的 dfs 算法都是比较形象的,很容易想象搜索过程,接下来我们看看一些需要抽象成 dfs 的问题。实际上,我们所遇到的大多数问题都是需要抽象成 dfs 的形式才能应用 dfs 进行搜索的。看下面一个问题。

给出 n 个整数,要求从里面选出 k 个整数,使的选出来的数的和为 S

  我们可以用 dfs 来抽象这样的枚举过程。在第一层 dfs 的时候,我们可以选择是否加上第一个数,如果加上第一个数,和值加上第一个数,dfs 进入到下一层,否则 dfs 直接进入到下一层,在第二层,对第二个数做同样的处理,dfs 的过程中记录已经选取的数的个数,如果已经选取了 k 个数,判断和值是否是 S。对于每一层,我们都有 2 个择,选和不选,不同的选择,都会使得搜索进入 2 个完全不同的分支进行搜索。

还是以一道题为例

买书

  小明去书店买书,他有 m 元钱,书店里面有 n 本书,每本书的价格为 pi 元。小明很爱学习,想把身上钱都用来买书,并且刚好买 k 本书。请帮小明计算他是否能刚好用 m 元买 k 本书


输入格式

第一行输入 3 个整数m(1m100000000)n(1n30)k(1kmin(8,n))

接下来一行输入 n 个整数,表示每本书的价格pi (1pi100000000)

输出格式

如果小明能 刚好 用 m 元买 k 本书,输入一行“Yes” 否则输出“No”

样例输入

10 4 41 2 3 4

样例输出

Yes

实现代码

import java.util.Scanner;public class Main{static int box [] = new int [40];static int a [] = new int [40];static int map [] = new int [40];private static int k;private static long m;private static int n;public static void main(String[] args) {Scanner cin = new Scanner(System.in); m = cin.nextLong(); n = cin.nextInt();k = cin.nextInt();for (int i = 0; i <n; i++) {a[i] = cin.nextInt();}dfs(a,0,0,0);System.out.println("No");}static void dfs(int [] a, int step,long sum,int pos){if(step>k||sum>m)return;if(step == k && sum == m){System.out.println("Yes");System.exit(0);return;}for (int i = pos; i <n; i++) {if(box[i] == 0){box[i] = 1;map[step] = a[i];dfs(a,step+1,sum+a[i],i+1);box[i] = 0;}}}}


八皇后问题

我们再来看一道很经典的问题—八皇后问题,这也是检验有一个人有没学会回溯法的问题。
小明在和朋友下国际象棋,下的时候突发奇想,在国际象棋棋盘的每个格子上写下 1 到 99 内的数字,又拿出了珍藏已久的 8 个皇后棋子。国际象棋中的皇后可以将同一行、同一列和同一对角线上的对方棋子吃掉。小明在想,怎么摆放这 8 个皇后的位置才能让她们不能互相攻击,同时这 8 个皇后占的格子上的数字总和最大。
小明来求助热爱算法的你了,你能帮他算出答案吗?

输入格式

每个棋盘有 64 个数字,分成 8行 8 列输入,就如样例所示。棋盘上每一个数字均小于 100

输出格式

输出一个最大的总和

样例输入

1  2  3  4  5  6  7  89 10 11 12 13 14 15 1617 18 19 20 21 22 23 2425 26 27 28 29 30 31 3233 34 35 36 37 38 39 4041 42 43 44 45 46 47 4848 50 51 52 53 54 55 5657 58 59 60 61 62 63 64

样例输出

260

实现代码

import java.util.Scanner;public class Main {private static int n;static int a[] = new int [100];static int box [] = new int [100];static int max = -1;private static int map[][] = new int [8][8];public static void main(String[] args) {Scanner cin = new Scanner(System.in);for (int i = 0; i <8; i++) {for (int j = 0; j <8; j++) {map[i][j] = cin.nextInt();}}Dfs(0);System.out.println(max);}static void Dfs( int step ){ //行if(step == 8){int sum = 0;for (int i = 0; i <step; i++) {sum+=map[i][a[i]];}if(sum>max) max = sum;return;}for (int i = 0; i <8; i++) {//列boolean isduijiaoxian = false;for (int j = 0; j <step; j++) //行if(Math.abs(a[j]-i)==Math.abs(j-step)){//判断是否在同一个对角线isduijiaoxian = true;break;}if(box[i] == 0&&isduijiaoxian == false){box[i] = 1;a[step] = i;Dfs(step+1);box[i] = 0;}}}}




原创粉丝点击