動態規劃,以LeetCode-CombinationSumIV問題爲例

簡介:面試

動態規劃問題面試中常常遇到的問題之一,按照動態規劃的通常定義,其通常解法在於將大問題分解爲不少小問題去解決,可是我在遇到不少實際的問題時,想法都是強行的去將問題分解,而忽略了分解的必要性和途徑的合理性。看某知乎大佬的帖子:動態規劃的核心思想在於分解的小問題可否被上一級的問題去重用,也就是說咱們在將大問題分解爲小問題時,要考慮到求解出的小問題對於大問題的求解是否有必定的做用並且求解小問題的過程對大問題須要沒有任何影響(像不像封裝,彷佛好多理論都是大同小異的,核心思想都很類似)。數組

例子:spa

以LeetCode-Combination Sum IV問題爲例:
設計

題目是這樣描述的:code

Given an integer array with all positive numbers and no duplicates, find the number of possible combinations that add up to a positive integer target.blog

Example:遞歸

nums = [1, 2, 3]
target = 4

The possible combination ways are:
(1, 1, 1, 1)
(1, 1, 2)
(1, 2, 1)
(1, 3)
(2, 1, 1)
(2, 2)
(3, 1)

Note that different sequences are counted as different combinations.

Therefore the output is 7.

是否是看起來感受毫無難度,遞歸嘛,一層一層下去不就行了!因此我寫出了遞歸求解思路:
1 private int combinationSum4_recur(int[] nums, int target){
2         int sum = 0;
3         for(int i : nums){
4             if(i == target) sum += 1;
5             else if(i < target) sum += combinationSum4_recur(nums, target - i);
6         }
7         return sum;
8 }

和咱們用腦子解決這個問題的思路如出一轍,而後就TLE了。。。索引

出錯的例子:nums={2,1,3} target=35,我日哦,若是用大腦去解決,你得想吐了。get

因此我須要想一想這是爲何,首先1+1,而後+1,而後+1.。。。。。而後好多好多+1,emmmm,出來了一個35的方案了,而後下一輪首先1+1,而後+1,而後+1.。。。。。而後好多好多+1再+3。等一下前面的1+1+1什麼的是否是很熟悉!咱們爲何不把它記住呢!因此,dp[]來了!it

dp[]數組是什麼呢,它是一個長度爲target+1的int數組,首先,0爲1;它的意思就是默認nums數組中組合爲0的可能方式設定爲1.而後,dp[1]等一系列元素咱們暫時設置爲-1,表示還未進行計算。這樣,由頂而下的動態規劃就出來了,咱們將求解一個巨大的target分解爲求解一個較小的target,而求解這個較小的target又會被分解爲求解一個更小的target。。。。。。直到最後,而在這過程當中,求解出來的target咱們都存儲在了dp數組中!那麼求解到最後,咱們豈不是一直進行數組的調用就能夠了!只進行了不多的計算!代碼以下:

 1 private int combinationSum4_dp(int[] nums, int target){
 2         dp = new int[target + 1];
 3         Arrays.fill(dp, -1);
 4         dp[0] = 1;
 5         return helper(nums, target);
 6 }
 7 
 8 private int helper(int[] nums, int target){
 9         /**
10          * dp記錄到達索引指示的target有幾種方案去解決
11          * 就是至關於記住它的中間結果!!!
12          */
13         if (dp[target] != -1) {
14             return dp[target];
15         }
16         int res = 0;
17         for (int i = 0; i < nums.length; i++) {
18             if (target >= nums[i]) {
19                 res += helper(nums, target - nums[i]);
20             }
21         }
22         dp[target] = res;
23         return res;
24}

是否是清晰不少,咱們首先進行了較小target的組合數,而後依次往大再往大。。。。。。這樣到最後咱們就解決了那個看似很大的target。這不就是動態規劃的思想嗎!

因此之後首先要找到大問題和小問題之間共有的特性,列出必定的狀態轉移規律,而後設計知足條件的小問題解決方案,最後憑藉記憶中的中間值快速求出最終解!

固然動態規劃問題極多,有待後續繼續進步。。。。。。

相關文章
相關標籤/搜索