494. Target Sum

一、题目简述

给定非负整数序列, a1,a2,...,an 和一个目标数 S ,现在你有两种符号+ ,对于每个整数,可以选择正号或负号作为新的符号。
找出为目标数S的符号组合方式的种数。
示例1

  • 输入: nums is [1, 1, 1, 1, 1], S is 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
    共有5种符号组合方式,是的序列之和为3。

注意:

  1. 给定序列长度为正且不会超过20。
  2. 给定序列元素之和不会超过1000。
  3. 输出将会使用32位整数表示。

函数原型:
int findTargetSumWays(vector<int>& nums, int s)

二、编程思路

dp[i][j]表示前i个数和为j的方案数。
则状态转移方程为 dp[i][j]=dp[i1][ja[i]]+dp[i1][j+a[i]]
为了避免负数的情况,可以对原始问题进行如下转化:

  • 令P为序列中符号为正的数字的集合,N为序列中符号为负的数字的集合,则
  • sum(P)sum(N)=target
  • sum(P)+sum(N)+sum(P)sum(N)=target+sum(P)+sum(N)
  • 2sum(P)=target+sum(P)+sum(N)
  • 即在原序列中寻找一个正数集合 P ,使得sum(P)=(target+sum(P)+sum(N))/2

根据上面推导过程可以看出, target+sum(P)+sum(N) 一定要是偶数,否者不存在符合条件的P。
则令 dp[i][j] 表示序列中从0到j的子序列中,其和为j的组合数,则有如下状态转移方程:

  • dp[i][j]=dp[i1][j]+dp[i1][jnums[i]]
  • dp[i][j]=0,j=0
    其中,前半部分代表不选择nums[i]的情况,后半部分代表选择nums[i]的情况。

三、程序设计

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int s) {
        int sum = accumulate(nums.begin(), nums.end(), 0);
        //(s + sum) & 1,判断s + sum的奇偶;(s + sum) >> 1,即(s + sum)/2
        return sum < s || (s + sum) & 1 ? 0 : subsetSum(nums, (s + sum) >> 1); 

    }   
    int subsetSum(vector<int>& nums, int s) {
        int dp[s + 1] = { 0 };
        dp[0] = 1;
        for (int n : nums)
            for (int i = s; i >= n; i--)
                dp[i] += dp[i - n];
        return dp[s];
    }
};

四、实验心得

  1. 在动态规划问题中需要找到合适的子问题分解方法,即本题目中dp的含义,确定其变量数目。
  2. 确定状态转移方程,将问题转换为子问题进行求解。
  3. 动态规划与递归都采用分治思想。动态规划是一个自底向上的过程,递归是一个自顶向下的过程。
    • 动态规划:动态规划的本质是状态状态转移方程的定义。如果一种状态定义满足无后效性,那么称之为具有最优子结构性质。两种性质都蕴含于状态转移方程之中。动态规划一般针对最优化问题
      • 最优子结构性质:每个阶段的最优状态可以从之前某个阶段的某个或某些状态直接得到。
      • 无后效性:不管之前这个状态是如何得到的。
      • 缺点:空间需求大。
    • 递归(分治):将原始问题分解为结构相似的子问题去解决,解决通用问题的思想。
      • 缺点:子问题可能存在重复。

参考资料

LeetCode题解
知乎·什么是动态规划?动态规划的意义是什么?

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值