• 【算法】蓝桥杯dfs深度优先搜索之排列组合总结


      为了重申感谢之意,再次声明下文的大部分灵感均来自于【CSDN】梅森上校《JAVA版本:DFS算法题解两个例子(走迷宫和求排列组合数)》
      强烈大家去上面那篇文章看看,写的很好。
      下面我会列出蓝桥杯第六届B组省赛第7题、第七届第5题、第八届第4题,共3道题。

      因为他们都是:排列组合。

    【第一道题】

    牌型种数
      这道题可以强制转为昨天的“凑算式”类型。
      首先,强调一下题意,总共13种牌A到K,每种可以选0到4张,总共选出13张,两个13如果简单表示的话就是2 13,其中13也可以用大写的字母B表示,隐晦的透露了这道题的内涵。
      如果你还能想起来昨天“凑算式”的思路的话,那么上来第一件事肯定就是设置一个数组了
      下图是我昨天在最后一题做的总结,对于这道题来说,也适合。
    步骤
      第一件事,显然这个数组的长度为13,因为我们要存13种牌,数组中只存0到4之间的数。

    public static int[] a = new int[13];
    

      第二件事,这里不涉及到数字重用与否,略过。
      第三件事,定义dfs方法,还是和昨天一样,就传一个index参数

    public static void dfs(int index)
    

      第四件事,写递归结束条件,这里就是index == 13,越界,代表A到K我们已经取完了,接下来就是要统计一下总数是不是13张。如果是的话,就算一种,count++。

    // 递归结束条件
    if(index == 13) {
    	int sum = 0;
    	for(int i : a) {
    		sum += i;
    	}
    	if(sum == 13) {
    		count++;
    	}
    	return; //递归结束一定要有return啊,没有return不叫递归结束
    }
    

      第五件事,还未凑齐,深搜。a[]数组总共13个位置,每个位置是0到4中的一个数。代码如下:

    // 搜索
    for(int i=0; i<=4; i++) {
    	a[index] = i;
    	dfs(index+1); 
    }
    

    【完整代码】

     1 public class 牌型种数dfs {
     2     public static int count = 0 ;
     3     public static int[] a = new int[13];
     4     public static void dfs(int index) {
     5         if(index == 13) {
     6             int sum = 0;
     7             for(int i : a) {
     8                 sum += i;
     9             }
    10             if(sum == 13) {
    11                 count++;
    12             }
    13             return;
    14         }
    15         // 搜索
    16         for(int i=0; i<=4; i++) {
    17             a[index] = i;
    18             dfs(index+1); 
    19         }
    20     }
    21 
    22     public static void main(String[] args) {
    23         dfs(0);
    24         System.out.println(count); // 答案是: 3598180
    25     }
    26 
    27 }

      其实我的这种解法,关键就在于对数组的使用是否熟练,用13个位置代表13个种类,每个位置只能填0到4,最后数组凑填满后,统计一下每个位置之和是否是13。
      如果你每天吃饭、睡觉、聊天都是讨论的和数组呀,dfs呀相关的,再加上看我写的文章,照着代码敲敲,那么用不了1天,准能掌握这种套路。

      这篇文章的标题是关于排列组合的,之所以开个新坑,就是想告诉大家,虽然我总结的步骤对大多数dfs类型的题有用,但是不要局限以为只有那样的模式才算是dfs。
      比如同样是这道题,同样是dfs算法,但是代码却不一样。下面的代码参考自【CSDN】h1021456873《蓝桥杯 牌型种数 (暴力||dfs)》

     1 public static int count = 0 ;
     2 public static void dfs(int type, int sum) {
     3     // 结束条件
     4     if(type == 13) { // A到K  13类
     5         if(sum == 13) { // 要凑够13张
     6             count++;
     7         }
     8         return;
     9     }
    10     // 搜索
    11     for(int i=0; i<=4; i++) {
    12         dfs(type+1, sum+i); // 此解法的关键,就在于sum+i 而不是sum+1
    13     }
    14 }
    15 
    16 public static void main(String[] args) {
    17     dfs(0,0);
    18     System.out.println(count);
    19 }

      可以看到这个dfs方法传入了两个参数,上面的代码没有像我那样使用数组,如果看懂我的代码,这个也挺好理解的。
      之所以要说上面的代码是要引出来下面这道题

    【第二道题】

    第二道题
      这是一道填空题,给出的代码如下,其中的注释是我添加的

     1 public class 抽签dfs {
     2     
     3     public static void f(int[] a, int k, int n, String s) {
     4         // 结束条件
     5         if (k == a.length) {
     6             if (n == 0)
     7                 System.out.println(s);
     8             return;
     9         }
    10         // 搜索
    11         String s2 = s;
    12         for (int i = 0; i <= a[k]; i++) {
    13             _________________________// 填空位置
    14             s2 += (char) (k + 'A');
    15         }
    16     }
    17 
    18     public static void main(String[] args) {
    19         int[] a = { 4, 2, 2, 1, 1, 3 };
    20         f(a, 0, 5, "");
    21     }
    22 }

      我还清楚的记得我第一次做这道题,当时我还不知道什么是dfs深度优先搜索,压根没看出来这代码什么意思,只是觉得应该递归。经过上篇文章的磨练,现在可以一眼看出这就是dfs的代码套路,只不过他传的参数有点多,4个。
      这道题13分,这种填空题一定不能莽撞,他给出了程序代码,自己填上答案之后,可以结合题意验证一下,比如这道题他有说明总共会输出101行结果,这就是一个检验条件。
      我第一次做的时候,完全是蒙的答案,如下:

    f(a, k++, n, s2);  //错误示例
    

    正确答案

    f(a, k + 1, n - i, s2); 
    

      很显然,我当时没有搞懂dfs的搜索代码,即下列代码

    for (int i = 0; i <= a[k]; i++) {
    	_________________________// 填空位置
    	s2 += (char) (k + 'A');
    }
    

      既然他在main方法中调用了dfs算法,参数n传入的是5,那么就代表观察团的总人数要求是5人,这里的for循环进行搜索,一但选中 i 个人,那么接下来只能选 n - i 个人,所以参数应该是n - i,而不是n
      还有一点就是对于深搜这种,下一个情况是k+1,而不能用k++,或++k。原因是数组会越界,至于为什么会越界,我自己分析了一下,没搞懂。最后就硬记住了,这就是套路,请按套路出牌。
      说实话,这道题如果不是填空题,而是一道大题,尽管我自认为理解了dfs算法,但还是写不对代码。还是要多理解理解这道题。

    【第三道题】

      这篇文章的最后一道题
    魔方状态
      先说明,这道题到底怎么解,其实我也不知道,在这里写它的原因是看到了下面这篇文章,不过作者说的答案:216,作者明知11112233和33221111是同一种知道去重,却没说出来12233111 和 11133221这样之类的也是同一种,因此对于他的答案我不敢苟同。
    【CSDN】sangjinchao《第八届蓝桥杯JAVAB组第四题》
      不过,就11112233全排列,这一单纯的知识点我是很感兴趣的。
      下面我想讨论一下使用dfs算法就给定数字全排列问题,比如上面的数字四个1两个2两个3进行全排列,我使用了标记法,写的代码如下

     1 public class 全排列dfs {
     2 
     3     public static int[] a = new int[] { 1, 1, 1, 1, 2, 2, 3, 3 };
     4     public static int[] visited = new int[8];
     5     public static int[] result = new int[8];
     6     public static void dfs(int index) {
     7         // 结束条件
     8         if (index == 8) {
     9             for (int i : result) {
    10                 System.out.print(i);
    11             }
    12             System.out.println();
    13             return;
    14         }
    15         // 搜索
    16         for(int i=0; i<8; i++) {
    17             if(visited[i]==0) {
    18                 visited[i] = 1;
    19                 result[index] = a[i];
    20                 dfs(index+1);
    21                 visited[i] = 0;
    22             }
    23         }
    24     }
    25 
    26     public static void main(String[] args) {
    27         dfs(0);
    28     }
    29 
    30 }

      不过,有些情况11112233和33221111,还有11221133和33112211这类的都算重复的,所以需要去掉。目前我给出一个不太成熟的代码,只能想到这里了,如果有谁有优化的代码,一定要给我打call告诉我

     1 public class 全排列dfs逆置去重 {
     2 
     3     public static int[] a = new int[] { 1, 1, 1, 1, 2, 2, 3, 3 };
     4     public static int[] visited = new int[8];
     5     public static int[] result = new int[8];
     6     public static int[] res = new int[33221112];
     7     public static int count = 0;
     8     public static void dfs(int index) {
     9         // 结束条件
    10         if (index == 8) {
    11             String s = "";
    12             String rev = "";
    13             StringBuilder sb= new StringBuilder();
    14             for (int i : result) {
    15                 sb.append(i);
    16             }
    17             s = sb.toString();
    18             rev = sb.reverse().toString(); // 逆置
    19             if(res[Integer.parseInt(rev)] == 0) {// 去重
    20                 res[Integer.parseInt(s)] = 1;
    21                 System.out.println(s);
    22                 count++;
    23             }
    24             return;
    25         }
    26         // 搜索
    27         for(int i=0; i<8; i++) {
    28             if(visited[i]==0) {
    29                 visited[i] = 1;
    30                 result[index] = a[i];
    31                 dfs(index+1);
    32                 visited[i] = 0;
    33             }
    34         }
    35     }
    36 
    37     public static void main(String[] args) {
    38         dfs(0);
    39         System.out.println(count);
    40     }
    41 
    42 }

      这篇文章到这里就该结束了,我的初衷就是想告诉大家,dfs不仅仅是我在上篇文章里面写的那篇,只能计算“凑算式”,dfs身为一种暴力破解方法,有很多种变形,还需要大家多加练习。
      有些人会担心,都这个时候了复习蓝桥杯,迟吗?送你一句话:Latter Better Than Never!
      
      上一篇文章 → 《【算法】蓝桥杯dfs深度优先搜索之凑算式总结》

      下一篇文章预计会在周五更新 → 《【算法】蓝桥杯dfs深度优先搜索之图连通总结》
      


    参考文章:

        【CSDN】h1021456873《蓝桥杯 牌型种数 (暴力||dfs)》

        【CSDN】豌豆苞谷《2017 第八届蓝桥杯 魔方状态》

        【CSDN】sangjinchao《第八届蓝桥杯JAVAB组第四题》

  • 相关阅读:
    unix文件权限
    jira部署,主机迁移,数据库迁移,jira
    c函数习记
    常用软介质下载
    Matlab interpgui
    LightOJ 1422
    【CODEFORCES】 A. Keyboard
    leetcode 230: Kth Smallest Element in a BST
    Vertica7 Native Connection Load Balance
    vlc模块间共享变量
  • 原文地址:https://www.cnblogs.com/littlecurl/p/10575638.html
Copyright © 2020-2023  润新知