• 494. Target Sum


    问题:

    给定一组非负整数nums,和一个目标数S,求给nums的各个元素添加符号后,使得和为S的可能性有多少?

    Example 1:
    Input: nums is [1, 1, 1, 1, 1], S is 3. 
    Output: 5
    Explanation: 
    -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
    There are 5 ways to assign symbols to make the sum of nums be target 3.
     
    Constraints:
    The length of the given array is positive and will not exceed 20.
    The sum of elements in the given array will not exceed 1000.
    Your output answer is guaranteed to be fitted in a 32-bit integer.
    

      

    解法:DP(动态规划) 0-1 knapsack problem(0-1背包问题)

    首先,由于S的符号不确定,我们可以将问题转换为:

    ●将数组分为正数组和负数组,使得正数组的和为一个定值的可能性有多少种?

    推导:

    sum(P) - sum(N) = target
    sum(P) + sum(N) + sum(P) - sum(N) = target + sum(P) + sum(N)
                           2 * sum(P) = target + sum(nums)

    所以:sum(P) = (target + sum(nums)) / 2

    因此,计算正数组和sum(P),由上式,首先排除奇数结果

    if (target + sum(nums))%2==1 then return 0

    由于本题数位限制,int可能不足->

    if (target ^ sum(nums))&1==1 then return 0

    另外,若给数组全部赋正号或全部赋负号,

    S不能>sum(nums) , S不能<-sum(nums) 

    因此有:

    if((newsum^S)&1 || S>newsum || S<-newsum) return 0;

    接下来,对●问题的DP进行分析:

    1.确定【状态】:

    • 可选择的数:前 i 个数
    • 和:s:0~newsum

    2.确定【选择】:

    • 选择当前的数nums[i]
    • 不选择当前的数nums[i]

    3. dp[i][s]的含义:

    前 i 个数中,组成和=s 的可能性的个数。

    4. 状态转移:

    dp[i][s]= SUM {

    • 选择 nums[i]:dp[i-1][s-nums[i]]:=前 i-1 个元素可组成和为 s-nums[i] 的可能性个数
    • 不选择 nums[i]:dp[i-1][s]:=前 i-1 个元素可组成和为 s 的可能性个数

    }

    5. base case:

    • dp[0][s]=false
    • dp[i][0]=true
    • dp[0][0]=true

    ♻️ 优化:去掉 i ,

    将s倒序。

    代码参考:

     1 class Solution {
     2 public:
     3     //Eq1:Positive+Negative=sum
     4     //Eq2:Positive-Negative=S
     5     //Eq1+Eq2:2*Positive=sum+S
     6     //Positive=(sum+S)/2
     7     //to find S-> to find subset which sum=Positive
     8     
     9     
    10     //dp[i][s]: in first i items, the number of ways whose sum is s
    11     //case_1,choose i-th item: dp[i-1][s-val[i]]
    12     //case_2,don't choose: dp[i-1][s]
    13     //dp[i][s] = case_1 + case_2
    14     //base case: dp[0][s] = 0
    15     //dp[i][0] = 1
    16     //dp[0][0] = 1
    17     //
    18     int findTargetSumWays(vector<int>& nums, int S) {
    19         int newsum = 0;
    20         for(int n:nums){
    21             newsum+=n;
    22         }
    23         if((newsum^S)&1 || S>newsum || S<-newsum) return 0;
    24         newsum = (newsum+S)/2;
    25         vector<int> dp(newsum+1, 0);
    26         dp[0]=1;
    27         for(int i=1; i<=nums.size(); i++) {
    28             for(int s=newsum; s>=0; s--) {
    29                 if(s-nums[i-1]>=0) {
    30                     dp[s] += dp[s-nums[i-1]];
    31                 }
    32             }
    33         }
    34         return dp[newsum];
    35     }
    36 };
  • 相关阅读:
    js引用类型赋值不改变原对象值
    VS2017启动实例调试(谷歌浏览器)闪退问题
    ext6时间控件(带时分秒)
    extjs列表中文件上传与下载(带有重命名操作)
    c# word(1) 向标签处添加文字
    关于页面加载后执行使用afterrender
    ExtJS,grid多选框列
    vue起手式
    Javascript诞生与历史
    markdown语法说明
  • 原文地址:https://www.cnblogs.com/habibah-chang/p/13582908.html
Copyright © 2020-2023  润新知