494. Target Sum

 题目意思很明确,最简单的是暴力法,对于每个元素nums[i],考虑+nums[i]和-nums[i]两种情况:

public class Solution {
    int count = 0;
    public int findTargetSumWays(int[] nums, int S) {
        calculate(nums, 0, 0, S);
        return count;
    }
    public void calculate(int[] nums, int i, int sum, int S) {
        if (i == nums.length) {
            if (sum == S)
                count++;
        } else {
            calculate(nums, i + 1, sum + nums[i], S);
            calculate(nums, i + 1, sum - nums[i], S);
        }
    }
}

此外,可以用动态规划:

对于每一种可能的情况,总由一些正数项和负数项相加而成,记整数项之和为S(P),负数项之和为Sum(N) ,则Sum(P)-Sum(N)=target,此外,记数组nums元素之和为Sum,则Sum(P)+Sum(N)=Sum,则有Sum(P)=(Sum+target)/2。所以,此问题可以转化为求数组nums中,能够被划分成和为(Sum+target)/2的子集的个数。(集合划分问题)

public class Solution {
	public int findTargetSumWays(int[] nums, int S) {
		int sum = 0;
		for (int i = 0; i < nums.length; i++) {
			sum += nums[i];
		}
		if (S > sum || (sum + S) % 2 == 1)
			return 0;
		return subsetSum(nums, (sum + S) / 2);
	}
 
	private int subsetSum(int[] nums, int S) {
		int[] dp = new int[S + 1];
		dp[0] = 1;//C(0,0)=1
		for (int i = 0; i < nums.length; i++) {
			for (int j = S; j >= nums[i]; j--) {
				dp[j] += dp[j - nums[i]];
			}
		}
		return dp[S];
	}
}

还有另一种动态规划的思路:定义dp[i][j]为前i个元素组成sum为j的方法次数,则:

dp[i][j+nums[i]]+=dp[i-1][j],dp[i][j-nums[i]]+=dp[i-1][j]。 

class Solution {
    public int findTargetSumWays(int[] nums, int S) {
        // backtrack
        // return backtrack(nums, 0, S, 0);
        if (nums == null || S > 1000 || S < -1000) {
            return 0;
        }
        
        int sum = 0;
        for (int n : nums) {
            sum += n;
        }
        
        int[][] dp = new int[nums.length][sum * 2 + 1]; // [-sum, sum]
        dp[0][nums[0] + sum] = 1;
        dp[0][-nums[0] + sum] += 1;
        
        for (int i = 1; i < nums.length; i++) {
            for (int n = -sum; n <= sum; n++) {
                if (dp[i - 1][n + sum] > 0) {
                    dp[i][n + sum + nums[i]] += dp[i - 1][n + sum];
                    dp[i][n + sum - nums[i]] += dp[i - 1][n + sum];
                }
            }
        }
        
        return S > sum ? 0 : dp[nums.length - 1][S + sum];
    }
    
//     private int backtrack(int[] nums, int index, int S, int curr) {
//         if (index == nums.length) {
//             return S == curr ? 1 : 0;
//         }
        
//         return backtrack(nums, index + 1, S, curr + nums[index]) + backtrack(nums, index + 1, S, curr - nums[index]);
//     }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值