题目:
小明被劫持到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 }
这道题目给我的思考是,在用暴力法的时候直接多想几种可能,多想几种优化方案,这样可以很大程度上节约时间