zoukankan      html  css  js  c++  java
  • 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 };
  • 相关阅读:
    cocos2dx的内存管理机制
    cocos2d-x多分辨率适配方案:setDesignResolutionSize使用
    在lua脚本中使用我们自定义的精灵类
    cocos2d-x内存管理(见解)
    MongoDB学习笔记(2):数据库操作及CURD初步
    剑指Offer:树的子结构
    剑指Offer:反转链表
    剑指Offer:调整数组顺序使奇数位于偶数前面【21】
    剑指Offer:链表中倒数第k个结点【22】
    剑指Offer:合并两个排序的链表【25】
  • 原文地址:https://www.cnblogs.com/habibah-chang/p/13582908.html
Copyright © 2011-2022 走看看