上一篇文章 → 《【算法】蓝桥杯dfs深度优先搜索之凑算式总结》java
为了重申感谢之意,再次声明下文的大部分灵感均来自于【CSDN】梅森上校《JAVA版本:DFS算法题解两个例子(走迷宫和求排列组合数)》
强烈你们去上面那篇文章看看,写的很好。
下面我会列出蓝桥杯第六届B组省赛第7题、第七届第5题、第八届第4题,共3道题。算法
由于他们都是:排列组合。数组
这道题能够强制转为昨天的“凑算式”类型。
首先,强调一下题意,总共13种牌A到K,每种能够选0到4张,总共选出13张,两个13若是简单表示的话就是2 13,其中13也能够用大写的字母B表示,隐晦的透露了这道题的内涵。
若是你还能想起来昨天“凑算式”的思路的话,那么上来第一件事确定就是设置一个数组了
下图是我昨天在最后一题作的总结,对于这道题来讲,也适合。
第一件事,显然这个数组的长度为13,由于咱们要存13种牌,数组中只存0到4之间的数。app
public static int[] a = new int[13];
第二件事,这里不涉及到数字重用与否,略过。
第三件事,定义dfs方法,仍是和昨天同样,就传一个index参数优化
public static void dfs(int index)
第四件事,写递归结束条件,这里就是index == 13,越界,表明A到K咱们已经取完了,接下来就是要统计一下总数是否是13张。若是是的话,就算一种,count++。ui
// 递归结束条件 if(index == 13) { int sum = 0; for(int i : a) { sum += i; } if(sum == 13) { count++; } return; //递归结束必定要有return啊,没有return不叫递归结束 }
第五件事,还未凑齐,深搜。a[]数组总共13个位置,每一个位置是0到4中的一个数。代码以下:spa
// 搜索 for(int i=0; i<=4; i++) { a[index] = i; dfs(index+1); }
【完整代码】.net
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天,准能掌握这种套路。3d
这篇文章的标题是关于排列组合的,之因此开个新坑,就是想告诉你们,虽然我总结的步骤对大多数dfs类型的题有用,可是不要局限觉得只有那样的模式才算是dfs。
好比一样是这道题,一样是dfs算法,可是代码却不同。下面的代码参考自【CSDN】h1021456873《蓝桥杯 牌型种数 (暴力||dfs)》code
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组第四题》