动态规划解决目标和问题

98 阅读1分钟

目标和问题

给定一个数组nums[],给定目标值target,对于数组中的n个数据,可以在这些数前加“+”或者“-”,最终组成target的方式有多少种?

解题思路

假设在这些值前面加“+”的数的总和为x,那么在这些值前面加“-”的数的总和为sum-x,那么就有公式:

  • x-(sum-x) = target -> 2x-sum = target -> x = (target + sum) / 2

首先考虑几种极端情况:

  • 1、当sum的绝对值之和小于target的绝对值时,返回0;
  • 2、当x不是整数,也就是(target + sum)/2的结果不是整数的时候,返回0;

之后本题就转换成了0-1背包问题,求数组中的数组成x的方式有多少种。使用动态规划的方式解决:

  • 1、定义dp数组,dp[j]表示组成j的组合方式有dp[j]种;
  • 2、确定递推公式:
    得到nums[i]之后,凑成dp[j]就有dp[j-nums[i]]种方法。
  • 3、dp数组初始化:dp[0]应该为1,因为给数组中的元素前加“+”还是“-”,都是1种方法、
  • 4、确定遍历顺序:和0-1背包中类似,nums放在外循环,target放在内循环,并且内循环倒置。
  • 5、举例推导dp数组

代码如下

class Solution {
    public int findTargetSumWays(int[] nums, int target) {
        // 求和
        int sum = 0;
        for (int num :nums) {
            sum += num;
        }
        if (target > 0 && target > sum) return 0;
        if (target < 0 && target < -sum) return 0;
        if ((target + sum) % 2 != 0) return 0;
        int x = (target + sum) / 2; // 加法的总和
        if (x < 0) x = -x;
        int[] dp = new int[x + 1];
        dp[0] = 1;
        for (int i = 0; i < nums.length; i++) {
            for (int j = x; j >= nums[i] ; j--) {
                dp[j] += dp[j - nums[i]];
            }
        }
        return dp[x];
    }
}