leetcode 494. 分割等和子集 golang实现

描述
给定一个非负整数数组,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。
注意:

数组非空,且长度不会超过20。
初始的数组的和不会超过1000。
保证返回的最终结果能被32位整数存下。
思路
动态规划 和 背包问题
1. 背包问题转化
此问题可转化为 找两个子集 P + N = All(其中P 为正子集 N 为负子集 All为全集 )
使得sum(P) - sum(N) = S
我们两边加上 sum(P) + sum(N) 即
sum(P) - sum(N) + sum(P) + sum(N) = S + sum(P) + sum(N)
2sum(P) = S + sum(P) + sum(N) 因为sum(P) + sum(N) = sum(All)

那么 sum(P) = (S + sum(All)/2

显然 如果 S + sum(All) 为奇数的话 是不存在答案的 

这个问题就变成了 给定一个数组 求和target=(S + sum(All)/2 的子集有多少种 这个问题跟 leetcode 416. 分割等和子集非常类似 ,有兴趣可以点这里

2. 动态规划转移方程
dp[i][j] 表示 前i 个数字 和 = j有多少种方法
如果j >= num 则
dp[i][j] = dp[i-1][j] + dp[i][j - num]
否则
dp[i][j] = dp[i-1][j]

3. dp[0][0] = 1

实现
func findTargetSumWays(nums []int, S int) int {
	var l = len(nums)
	var sum int

	for _, num := range nums{
		sum += num
	}

	if sum < S || (S + sum)%2 == 1{
		return 0
	}

	target := (S + sum)/2

	dp := make([][]int, l + 1)
	dp[0] = make([]int, target + 1)
	dp[0][0] = 1

	for i := 1; i <= l; i++{
		dp[i] = make([]int, target + 1)
		for j := 0; j <= target; j++{
			if j >= nums[i-1]{
				dp[i][j] = dp[i-1][j] + dp[i-1][j-nums[i-1]]
			}else{
				dp[i][j] = dp[i-1][j]
			}
		}
	}

	return dp[l][target]
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值