1. 程式人生 > >494. Target Sum(轉換+dp)

494. Target Sum(轉換+dp)

題目:組成目標數有多少種方法?

思路:

舉例說明: 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)

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

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

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

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

如果 sum(nums) 小於 S或者 (sum(nums) + S) % 2 !=0 表示不能組成目標數,返回0。

應用dp解決子集合問題:
dp[y]+=dp[y-num],表示組成數值y的方案數等於原來組成數值y的方案數 + 組成數值y-num的方案數

例如
dp[8] = dp[8] + dp[8-1] (假設陣列為: 1 2 3 4 5)
dp[8] = dp[8] + dp[8-2]
dp[8] = dp[8] + dp[8-3]
dp[8] = dp[8] + dp[8-4]
dp[8] = dp[8] + dp[8-5]

dp程式碼

for(int num : nums) 
    for
(int y=target;y>=num;y--) dp[y]+=dp[y-num];

完整程式碼:

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int S) {
        int sum = 0;
        for(int num : nums) sum+=num;

        if (sum < S || (sum + S) % 2 != 0)
           return 0;

        int target = (sum+S)>>1
; int dp[target+1]; memset(dp,0,sizeof(dp)); dp[0] = 1; for(int num : nums) for(int y=target;y>=num;y--) dp[y]+=dp[y-num]; return dp[target]; } };