1. 程式人生 > >LeetCode-【動態規劃】-目標和

LeetCode-【動態規劃】-目標和

給定一個非負整數陣列,a1, a2, ..., an, 和一個目標數,S。現在你有兩個符號 + 和 -。對於陣列中的任意一個整數,你都可以從 + 或 -中選擇一個符號新增在前面。

返回可以使最終陣列和為目標數 S 的所有新增符號的方法數。

示例 1:

輸入: nums: [1, 1, 1, 1, 1], S: 3
輸出: 5
解釋: 

-1+1+1+1+1 = 3
+1-1+1+1+1 = 3
+1+1-1+1+1 = 3
+1+1+1-1+1 = 3
+1+1+1+1-1 = 3

一共有5種方法讓最終目標和為3。

注意:

  1. 陣列的長度不會超過20,並且陣列中的值全為正數。
  2. 初始的陣列的和不會超過1000。
  3. 保證返回的最終結果為32位整數。

題解:舉例說明: nums = {1,2,3,4,5}, target=3, 一種可行的方案是+1-2+3-4+5 = 3      該方案中陣列元素可以分為兩組,一組是數字符號為正(P={1,3,5}),另一組數字符號為負(N={2,4})      

因此: sum(1,3,5) - sum(2,4) = target              

sum(1,3,5) - sum(2,4) + sum(1,3,5) + sum(2,4) = target + sum(1,3,5) + sum(2,4)               

sum(1,3,5) = target + sum(1,3,5) + sum(2,4)              

sum(P) = target + sum(nums)              

sum(P) = (target + sum(nums)) / 2      

由於target和sum(nums)是固定值,因此原始問題轉化為求解nums中子集的和等於sum(P)的方案個數問題

接著這個分析,求陣列子集和的問題參考本部落格中的文章https://blog.csdn.net/zw159357/article/details/82805282,但本題基於子集和改變了一個條件,這裡求的是能組成子集和的情況有多少種,所以考慮怎樣描述這個未知量,設dp[i]表示數字和為i時對應的對應的數目,它的前一種狀態可表示為dp[i-nums[j]],這裡減去nums[j]的意思是減去當前數字對應的解,dp[i]相應就表示加上當前數字的最優解,最後i對應的值應該是sum(P);

class Solution {
     public int findTargetSumWays(int[] nums, int s) {
            int sum = 0;
            int n=nums.length;
            for(int i=0;i<n;i++)
                sum+=nums[i];
            if(s>sum||(sum+s)%2!=0)
                return 0;
            int t=(sum+s)/2;
            int[] dp=new int[t+1];
            dp[0]=1;
            for(int i=0;i<n;i++){
                for(int j=t;j>=nums[i];j--){
                    dp[j]+=dp[j-nums[i]];
                }
            }
            return dp[t];
    }  
}