动态规划之目标和问题(看不懂请给我寄刀片过来)

1.对应letecode链接:

494. 目标和 - 力扣(LeetCode)

2.题目描述:

 3.解题方法:

1.这是这个经典的背包问题每个元素前面要么添加-号要么添加+号也就是每个位置有两种选择要么前面添加+ 要么添加-号。

2.我们可以定义递归函数进行尝试:[index......nums.size()]范围内进行尝试也就是[0...index]范围内每个位置有两种选择能否让target变为0如果能够让它变为0说明就有一种方法。具体请看代码

4.对应代码:

暴力递归

class Solution {
  public:
    int findTargetSumWays(vector<int>& nums, int target) {
        //递归含义为[0......]一直到数组的结尾
        return process(nums, 0, target);
    }
    int process(vector<int>& nums, int index, int target) {
        if (index == nums.size()) {
            return target == 0 ? 1 : 0;
        }
        //每个位置有两种选择要么选择前面添加+号要么选择前面添加-号
        return process(nums, index + 1, target + nums[index]) + process(nums, index + 1,
                target - nums[index]);

    }
};

记忆化搜索

class Solution {
  public:
    unordered_map<int, unordered_map<int, int>>dp;
    int findTargetSumWays(vector<int>& nums, int target) {
        //递归含义为[0......]一直到数组的结尾
        return process(nums, 0, target);
    }
    int process(vector<int>& nums, int index, int target) {
        if (index == nums.size()) {
            return target == 0 ? 1 : 0;
        }
        if (dp.count(index) &&
                dp[index].count(target)) { //说明之前已经计算过了
            return dp[index][target];
        }
        //每个位置有两种选择要么选择前面添加+号要么选择前面添加-号
        int ans = process(nums, index + 1, target + nums[index]) + process(nums,
                  index + 1, target - nums[index]);
        dp[index][target] = ans;
        return ans;
    }
};

可能有小伙伴说这也太水了吧?确实博主我自己都快受不了了,下面我们来看一下优化:

优化一:我们可以将数组种的数全部变成正数对答案不会有影响,因为反正每个数要么前面添加+号要么前面添加-号。

优化二:我们可以将数组种所有的数都进行累加如果target比sum要大一定是0种方法,如果target和sum的奇偶性不一样也一定是0种方法。这是应为你是这些数搞成来的如果你和sum的奇偶性不一样肯定是0种方法。

优化三:对于这个优化下面我们举个例子

下面我们假设数组为{1,2,3,4,5} ,target=3.下面我们将前面取+号的单独拿出来,将前面取负的单独拿出来。分别为P,Q。

P={1,3,5},Q={2,4}。那么一定就有P中所有数的累加和-Q中所有数的累加和=target.即P-Q=target.我们同时在等式两边加上P+Q。

P+P+Q-Q=target+P+Q  -> 2*P=target+P+Q.而P+Q不就是刚好就是整个数组的累加和吗?也就是P=(sum+target)/2.从这里我们可以看出来一个P对应一个target。那么问题是不是就转换为在数组{1,2,3,4,5}中每个数有两种选择要么选要么不选能搞定P的数量。

对应优化代码:

class Solution {
  public:
    int findTargetSumWays(vector<int>& nums, int target) {
        int sum = 0;
        for (auto x : nums) {
            sum += x;
        }
        //如果目标比sum还要大或者两者的奇偶性不一样
        return (target > sum) ||
               (sum & 1) ^ (target & 1) ? 0 : Subset(nums, 0, (sum + target) / 2);
    }
    int Subset(vector<int>& nums, int index, int target) {
        if (index == nums.size()) {
            return target == 0 ? 1 : 0;
        }
        if (target < 0) {
            return 0;
        }
        //不选择当前位置的数
        int ways = Subset(nums, index + 1, target);
        //选择当前位置的数
        ways += Subset(nums, index + 1, target - nums[index]);
        return ways;
    }
};

记忆化搜索:

class Solution {
  public:
    vector<vector<int>>dp;
    int findTargetSumWays(vector<int>& nums, int target) {
        for (int i = 0; i < nums.size(); i++) {
            nums[i] = nums[i] < 0 ? -nums[i] : nums[i];
        }
        int sum = 0;
        for (auto x : nums) {
            sum += x;
        }
        if (target > sum || (sum - target) % 2 != 0) {
            return 0;
        }
        dp.resize(nums.size() + 1, vector<int>(sum + 1, -1));
        //(target+sum)/2的最大范围为target==sum时也就是sum
        return Subset(nums, 0, (sum - target) / 2);
    }
    int Subset(vector<int>& nums, int index, int target) {
        if (index == nums.size()) {
            return target == 0 ? 1 : 0;
        }
        if (target < 0) {
            return 0;
        }
        if (dp[index][target] != -1) {
            return dp[index][target];
        }
        //不选择当前位置的数
        int ways = Subset(nums, index + 1, target);
        //选择当前位置的数
        ways += Subset(nums, index + 1, target - nums[index]);
        dp[index][target] = ways;
        return ways;
    }
};

严格位置依赖的动态规划:

class Solution {
  public:

    int findTargetSumWays(vector<int>& nums, int target) {
        for (int i = 0; i < nums.size(); i++) {
            nums[i] = nums[i] < 0 ? -nums[i] : nums[i];
        }
        int sum = 0;
        for (auto x : nums) {
            sum += x;
        }

        if (target > sum || (sum & 1) ^ (target & 1)) {
            return 0;
        }
        return Subset(nums, 0, (sum - target) / 2);
    }
    int Subset(vector<int>& nums, int index, int target) {
        if (target < 0) {
            return 0;
        }
        int N = nums.size();
        vector<vector<int>>dp(N + 1, vector<int>(target + 1));
        dp[N][0] = 1;
        for (int i = N - 1; i >= 0; i--) {
            for (int j = 0; j <= target; j++) {
                dp[i][j] = dp[i + 1][j];
                if (nums[i] <= j) {
                    dp[i][j] += dp[i + 1][j - nums[i]];
                }
            }
        }
        return dp[0][target];
    }
};

猜你喜欢

转载自blog.csdn.net/qq_56999918/article/details/125739437