深度优先搜索(dfs)例题总结


一、部分和问题

题目描述:给定整数序列a1,a2,.....,an,判断是否可以从中选出若干数,使他们的和恰好为k。
       1<=n<=20
       -10^8 <= ai <= 10^8
       -10^8 <= k <= 10^8
样例:
输入
       n=4
       a={1,2,4,7}
       k=13
输出
       Yes (13=2+4+7)


思路分析:这道题目首先按照题目进行输入。之后调用dfs1()方法,将数组,k,以及数组的首位置传过去(作为0状态)。然后开始遍历位置,每一个位置都面临两种选择,第一种选择是取这个位置的值,第二种选择是不取这个位置的值。第一种选择不取这个位置的值就去查看下一个位置的值,就对应代码dfs1(arr,k,current+1);,只有状态加一,其余不变。第二种选择取这个位置的值,就将该位置的值存入list中,并获得index。然后调用代码dfs1(arr,k-arr[current],current+1);,可见k的值减去该位置的值,同样状态+1。这题有一个必要的点是要记得回溯,如果这条路走不通,要回溯到原来的状态,就是将该位置的值在list中删除,对应代码list.remove(index); 。最后一步就是确定出口,当k等于0时就输出结果。当k<0或者current==arr.length时说明这条路走不通,返回继续寻找其他路径。

static int kk;
    static ArrayList list=new ArrayList<>();           //存放计算过程
    //部分和问题
    public static void main(String[] args) {
        Scanner reader=new Scanner(System.in);
        
        int n=reader.nextInt();
        int[] arr=new int[n];
        for(int i=0;i


二、水洼数目问题

题目描述:有一个大小为NM的园子,雨后积起了水,八连通的积水被认为是连接在一起的。请求出园子里总共有多少水洼?(八连通值得是下图中相对W的*的部分)
       ***
       *W*
       ***
限制条件:
       N,M<=100
样例:
输入
       N=10,M=12
园子如下图:

vfew

输出
       3
*

思路分析:这道题目首先按照题目进行输入,将园子存入字符数组中。之后循环遍历园子,查看那个点位存在水洼,就调用dfs2()方法去清除与该点连通的水洼。方法中,以(i,j)为起点,有8个位置可以行走,但是不能重复上一层,故要先清除(i,j)位上的水洼。然后8个位置分为就是k=-1,0,1,j=-1,0,1.然后要排除k=0,j=0的情况,并且注意越界。如果(i+k,j+q)存在连通水洼,则调用方法清除水洼。最后输出结果

// 水洼数目问题
    static char[][] arr;                            //字符数组,存放园子
    static int N, M;    
    static int count=0;                             //记录水洼数目                            
    
    public static void main(String[] args) {
        Scanner reader = new Scanner(System.in);
        N = reader.nextInt();
        M = reader.nextInt();
        arr = new char[N][];
        
        for (int i = 0; i < N; i++) {
            arr[i] = reader.next().toCharArray();   //初始化园子
        }
        
        for (int i = 0; i < N; i++) {               //循环遍历园子的每一个位置  
            for (int j = 0; j < M; j++) {
                if (arr[i][j] == 'W') {             //如果是W
                    dfs2(i, j);                     //调用函数,清除水洼
                    count++;                        //水洼数+1
                }
            }
        }
        System.out.println(count);
    }

    private static void dfs2(int i, int j) {
        arr[i][j] = '.';                            //清除该水洼
        
        //遍历查询与该点连通的水洼
        for (int k = -1; k < 2; k++) {              //-1,0,1        
            for (int q = -1; q < 2; q++) {          //-1,0,1    
                if (k == q && q == 0) {
                    continue;
                }
                if (i + k >= 0 && j + q >= 0 && i + k <= N - 1 && j + q <= M - 1) {         //限定范围
                    if (arr[i + k][j + q] == 'W') {         //如果存在连通的水洼
                        dfs2(i + k, j + q);                 //递归调用,清除水洼
                    }

                }
            }
        }
    }


三、n皇后问题

题目描述:请设计一种算法,解决著名的n皇后问题。这里的n皇后问题指在一个nn的棋盘上放置n个棋子,使得每行每列和每条对角线上都只有一个棋子,求其摆放的方法数。
给定一个int n,请返回方法数,保证n小于等于15
*

思路分析:这道题目首先按照题目进行输入,创建一个整形数组,用来放每一行皇后的位置,调用dfs3()方法,从第0行开始遍历。因为有n行,要放n个皇后所以肯定是每行一个皇后,故for (int i = 0; i < n; i++)用来遍历每一行中的每一个位置,for (int j = 0; j < row; j++)用来遍历数组中之前每一行的皇后的位置。然后对(row,i)位置判断是否能放皇后,如果不能放,就置judge为false。判断的条件主要有三个。1、同一列上不能有多个皇后。2、正对角线上不能有多个皇后(正对角线上x和y之和相等)3、反对角线上不能有多个皇后(反对角线上y-x的差相等)。如果judge为true,就将该位置存入整形数组,递归调用下一行。如果方法无法进行下去,就要回溯。最后要设置出口。

//n皇后问题
    static int[] arr;                                       // 整形数组,用来存放每一行皇后的位置
    static int count;                                       // 记录有多少种解法

    public static void main(String[] args) {
        Scanner reader = new Scanner(System.in);
        int n = reader.nextInt();
        arr = new int[n];

        dfs3(n, 0);                                         // 调用深度优先搜索

        System.out.println(count); // 打印结果
    }

    private static void dfs3(int n, int row) {
        if (row == n) {                                     //出口
            count++;
            return;
        }
        for (int i = 0; i < n; i++) {                       //遍历每一行中的每个位置
            
            boolean judge = true;                           //初始化
            
            for (int j = 0; j < row; j++) {                 //遍历row行以前的皇后的
                
                if (arr[j] == i || row + i == j + arr[j] || i - row == arr[j] - j) {    //判定该位置是否能放皇后
                    judge = false;                          //不能放皇后
                }
                
            }
            if (judge) {                                    //如果judge为true
                arr[row] = i;                               //将row行i号位放置皇后
                dfs3(n, row + 1);                           //递归调用下一行的皇后
                arr[row] = 0;                               //方法不成功,回溯
            }
        }
    }


四、素数环问题

题目描述:对于正整数n,对1~n进行排列,使得相邻两个数之和均为素数,输出时从整数1开始,逆时针排列。同一个环应恰好输出一次
n<=16
如输入:6
输出:
1 4 3 2 5 6
1 6 5 2 3 4

思路分析:这道题目首先按照题目进行输入,创建一个整形数组,用来存放素数环每一个位置的数字。首先将1存入数组,然后调用dfs4()方法。for (int i = 1; i < n + 1; i++)尝试将每一个数字填入到数组中,然后for (int j = 0; j < count; j++)循环遍历是否数组中已经存在这个数。如果不是,接着判断相邻两个数之和是否为素数,如果是素数,递归调用下一个数,将本次循环的数传给下一次循环,以便判断相邻的数。最后设置出口,要记得判断最后一个数字和第一个数字能否形成素数环,如果可以,就输出结果。

    // 素数环问题
    static int[] arr;                                       //整形数组,存放每一个位置的值
    static int count;                                       //数组中的个数

    public static void main(String[] args) {
        Scanner reader = new Scanner(System.in);
        int n = reader.nextInt();
        arr = new int[n];
        
        arr[0] = 1;                                         //将1存入数组
        count++;
        
        dfs4(n, 1);                                         //调用深度优先搜索
    }

    private static void dfs4(int n, int k) {
        if (count == n) {                                   //出口
            if (judgePrimeNumber(arr[0] + arr[n-1])) {      //判断最后一个数字和第一个数字之和是不是素数
                for (int i = 0; i < n ; i++) {
                    System.out.print(arr[i]);               //输出
                }
                System.out.println();
            }
            return;
        }
        for (int i = 1; i < n + 1; i++) {                   //尝试用每一个数加入到数组中
            boolean judge = true;
            for (int j = 0; j < count; j++) {               //判断这个数是否已经被选过
                if (i == arr[j]) {
                    judge = false;
                }
            }
            if (judge == true) {
                if (judgePrimeNumber(k+i)) {                //判断相邻的数之和是否为素数
                    arr[count] = i;                         //添加到数组中
                    count++;
                    dfs4(n, i);                             //递归调用下一个数
                    count--;
                    arr[count] = i;                         //回溯

                }
            }
        }

    }
    public static boolean judgePrimeNumber(int k) {          //判断是否为素数
        int q;
        for (q = 2; q < k; q++) {
            if (k % q == 0) {
                return false;
            }
        }
        return true;
    }


五、困难的串问题

题目描述:如果一个字符串包含两个相邻的重复子串,则称它为容易的串,其他的串称为困难的串。
如:BB,ADCDACABCAB,ABCDABCD都是容易的串,D,DC,ADBAB,CBABCBA都是困难的。
输入正整数n,L,输出由前L个字符组成的,字典序第n小的困难的串。
例如,当L=3时,前7个困难的串分别为:
A,AB,ABA,ABAC,ABACA,ABACAB,ABACABA
n指定为4的话,输出ABAC

思路分析:这道题目首先按照题目进行输入,创建一个count用来存放第几个。调用dfs5()方法。按照字典序遍历,然后调用isHard()方法判断是否把i加进去也构成苦难的串,如果成立,就将i添加到prefix的末尾,count++,继续递归调用,直到最后输出。判断苦难的串的方法,是从后往前遍历。

    //困难的串
    static int L,n;                                     
    static int count=0;                                     //用来记录第几个
    public static void main(String[] args) {
        Scanner reader=new Scanner(System.in);
        n=reader.nextInt();
        L=reader.nextInt();
        dfs5("");                                           //调用深度优先搜索
    }
    private static void dfs5(String prefix) {
        for(int i='A';i<='A'+L+1;i++) {                     //按照字典序遍历
            if(isHard(prefix,i)) {                          //判断将i加进字符串中,是否还构成困难的串
                String x=prefix+i;                          //将i加入字符串中
                count++;
                if(count==n) {                              //出口
                    System.out.println(x);
                    System.exit(0);
                }
                dfs5(x);                                    //递归调用,继续寻找
            }
        }
        
    }
    private static boolean isHard(String prefix, int i) {    //判断将i加进字符串中,是否还构成困难的串
        int cnt=0;
        for(int j=prefix.length()-1;j>=0;j-=2) {             //从末至尾判断是否是苦难的串
            String x1=prefix.substring(j,j+cnt+1);
            String x2=prefix.substring(j+cnt+1)+i;
            if(x1.equals(x2)) {                              //是否相同,则为容易的串
                return false;
            }
            cnt++;
        }
        return true;
    }

你可能感兴趣的:(深度优先搜索(dfs)例题总结)