目标和00

题目链接

目标和

题目描述

目标和00_第1张图片

注意点

  • 只能向数组中的每个整数前添加 ‘+’ 或 ‘-’
  • 返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目
  • 1 <= nums.length <= 20
  • 0 <= nums[i] <= 1000
  • 0 <= sum(nums[i]) <= 1000
  • 1000 <= target <= 1000

解答思路

  • 第一时间想到的是使用深度优先遍历寻找所有符合题意的组合,但是时间不理想
  • 可将本题看作背包问题,思路为:创建一个行为n + 1列为sum * 2 + 1的dp数组,dp[i][j]表示(0, i)之间能够得到和为(j - 1000)的组合数,列的大小为sum * 2 + 1的原因是数组中所有数字相加相减得到的最大值和最小值分别为sum和-sum,所以需要sum * 2 + 1容量用于存储和为负的情况,在dp中列为sum时视作数组和为0
  • dp[0][j]表示不选择数组中任何一个数,此时和只有一种情况,就是0,所以dp[0][sum]为1,其余dp[0][j]都为0
  • 可根据dp[i - 1][j]推出dp[i][j - nums[i - 1]]和dp[i][j + nums[i - 1]],但是要注意边界问题,也就是j - nums[i - 1]为负数和j + nums[i - 1]大于sum * 2时(实际数组中所有数字任意组合也不会超过这个边界)

代码

class Solution {
    public int findTargetSumWays(int[] nums, int target) {
        int n = nums.length;
        int sum = 0;
        for (int num : nums) {
            sum += num;
        }
        if (target - sum > 0 || target + sum < 0) {
            return 0;
        }
        // dp[i][j]表示(0, i)之间能够得到和为(j - 1000)的组合数
        int[][] dp = new int[n + 1][sum * 2 + 1];
        // 不取数字时和为0
        for (int i = 0; i <= n; i++) {
            dp[i][sum] = 1;
        }
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j < sum * 2 + 1; j++) {
                int comb = 0;
                if (j - nums[i - 1] >= 0) {
                    comb += dp[i - 1][j - nums[i - 1]];
                }
                if (j + nums[i - 1] < sum * 2 + 1) {
                    comb += dp[i - 1][j + nums[i - 1]];
                }
                dp[i][j] = comb;
            }
        }
        return dp[n][target + sum];
    }
}

关键点

  • 本题如何转换为背包问题
  • 背包问题的思路
  • 注意考虑边界问题

你可能感兴趣的:(算法TOP100,leetcode,java,算法,数据结构,动态规划)