題目:
小明被劫持到X賭城,被迫與其他3人玩牌。
一副撲克牌(去掉大小王牌,共52張),均勻發給4個人,每個人13張。
這時,小明腦子里突然冒出一個問題:
如果不考慮花色,只考慮點數,也不考慮自己得到的牌的先后順序,自己手里能拿到的初始牌型組合一共有多少種呢?
請填寫該整數,不要填寫任何多余的內容或說明文字。
拿到這道題的時候第一時間想到了解決方案:dfs。
但是我在編寫第一版的時候出現了很大的問題,導致算法復雜度為O(n^n),當然,這道題目里面的n自然就是13了。
我的想法是:從第一張牌開始取,一直取到第13張,而每一次取牌呢,是在13種牌里面遍歷,用一個數組記錄每一種牌目前被取了多少,在遍歷中看這個牌被取得數目是不是大於4,是的話就不取這張牌。
然而這么做直接導致13的循環里面每一次都有13種可能,再加上排列組合,一共13^13種需要遍歷。
代碼如下:
1 import java.util.Arrays; 2 3 public class Main_1 { 4 5 public static void main(String[] args) { 6 Solution s = new Solution(); 7 int[] numStates = new int[13]; 8 s.recurse(0, numStates); 9 System.out.println(s.allPos); 10 } 11 } 12 class Solution { 13 int allPos = 0; 14 public void recurse(int curStep, int[] numStates) { 15 if(curStep == 13) { 16 allPos++; 17 System.out.println(Arrays.toString(numStates)); 18 } 19 else { 20 for(int i = 2; i < 15; i++) { 21 if(numStates[i - 2] < 4) { 22 numStates[i - 2]++; 23 recurse(curStep + 1, numStates); 24 numStates[i - 2]--; 25 } 26 } 27 } 28 } 29 }
第一版的直接運行結果是半天沒有反應,我一開始以為它陷入死循環了,結果用“System.out.println(Arrays.toString(numStates));”一看,發現一直在跑,但是過於復雜了。
我看到這個結果以為不能用暴力法破解,然后去網上看看別人的做法,發現有人用暴力法成功了,我大致看了別人的代碼之后發現我的問題可能出自暴力法之中。
第二版我的想法是:還是遞歸13次,不過這次遞歸的是每一種牌的取得個數。也就是在13次的遞歸之中,每一次研究當前這種牌能取多少(具體一點就是比如說該考慮8這張牌了,那么有5種可能,從一張不拿到四張全拿),當遞歸次數達到13時,直接比較當前牌的總數是不是13。當然優化的方法是直接把另外一個條件也作為結束遞歸的標志:“目前的牌的總數大於了13,那么之后就算全部不取都無法滿足條件”。算法復雜度是O(5^n),這道題目里面n = 13;
代碼如下:
1 public class Main_2 { 2 3 public static void main(String[] args) { 4 Solution_2 s = new Solution_2(); 5 s.getResult(0, 0); 6 System.out.println(s.num); 7 } 8 } 9 10 class Solution_2 { 11 public int num = 0; 12 public void getResult(int curSum, int curStep) { 13 if(curStep == 13 || curSum > 13) { 14 if(curSum == 13) 15 num++; 16 } 17 else { 18 for(int i = 0; i <= 4; i++) { 19 getResult(curSum + i, curStep + 1); 20 } 21 } 22 } 23 }
這道題目給我的思考是,在用暴力法的時候直接多想幾種可能,多想幾種優化方案,這樣可以很大程度上節約時間