上一篇文章 → 《【算法】藍橋杯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組第四題》