Leetcode 494. 目标和 dp

地址 https://leetcode-cn.com/problems/target-sum/

给你一个整数数组 nums 和一个整数 target 。
向数组中的每个整数前添加 '+' 或 '-' ,然后串联起所有整数,可以构造一个 表达式 :
例如,nums = [2, 1] ,可以在 2 之前添加 '+' ,在 1 之前添加 '-' ,然后串联起来得到表达式 "+2-1" 。
返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。


示例 1:
输入:nums = [1,1,1,1,1], target = 3
输出:5
解释:一共有 5 种方法让最终目标和为 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
+1 + 1 + 1 + 1 - 1 = 3

示例 2:
输入:nums = [1], target = 1
输出:1
 

提示:
1 <= nums.length <= 20
0 <= nums[i] <= 1000
0 <= sum(nums[i]) <= 1000
-1000 <= target <= 1000

解答
1 暴力遍历 每个元素可以有正负两个选择 O(2^20)
2 dp 由于有负数不可作为索引。 将所有计算额外加上了1000。
3 dp 通过公式推导,变成了01背包。

方案1
dfs 暴力遍历每个元素前面添加+或者-
到最后查看得到的计算结果是否等于target
复杂度比较高. 每个元素有+-两个选择,一共20个元素,那么暴力遍历完成就是O(2^(nums.length))
2^20 = 2^10 * 2^10 = 1024*1024 约等于 10^6 勉强能不超时ac

class Solution {
public:
    int ans = 0;
    void dfs(vector<int>& nums,int idx,int sum,int target){
        if(idx == nums.size()){
            if(sum == target) ans++;
            return;
        }

        sum +=nums[idx];
        dfs(nums,idx+1,sum,target);
        sum -= 2*nums[idx];
        dfs(nums,idx+1,sum,target);
        return;
    }

    int findTargetSumWays(vector<int>& nums, int target) {
        dfs(nums,0,0,target);

        return ans;
    }
};

方案2
背包问题类型
dp[x][y] 表示在数组前x个元素中,计算得出结果为y的方案数。
由于可能出现负数,负数不能作为数字索引下标。所以整个数组计算的时候 右移了20000. 因为sum(nums[i]) <= 1000; 且nums.lenth<=20 ,最坏情况全部取负号,则20*1000=20000.稍微再多一点点防止越界所以取了20030;
也就是
dp[x][y+20030]表示在数组前x个元素中,计算得出结果为y的方案数。

记忆化搜索方案
函数 solve(nums, target,n)
表示 nums中的数组经过前n个元素选择正负号后 能得到等于target的方案数
对应的数组是dp[n][target+20030].
最开始将数组全部初始化为-1 ,表示不能,如果某个时候检测该数组不为-1 ,说明已经计算过,可以直接调用结果不必重复计算

时间复杂度是  O(n*sum(abs(nums[i]))) ; i=0~n-1

int solve(const vector<int>& nums, int target, int idx) {
        if (dp[idx][target+20030] != -1) return dp[idx][target+ 20030];
}
class Solution {
public:
    vector<vector<int>> dp;

    int solve(const vector<int>& nums, int target, int idx) {
        if (idx < 0) return 0;
        if (dp[idx][target+20030] != -1) return dp[idx][target+ 20030];
        int val = nums[idx];
        // 自顶向下搜索 
        // nums中的数组经过前面元素选择正负号后 当前最后一个元素等于0  能得到等于target=0的方案数, 那么肯定是2 疑问当前元素0 选择-0 +0 都可以
        if (idx == 0 && target == val && val == 0) { dp[idx][target + 20030] = 2; return  dp[idx][target + 20030]; }
        
        // nums中的数组经过前面元素选择正负号后 当前最后一个元素恰好等于target 的方案数 也就是选择+号  方案数为1
        if (idx == 0 && target == val) { dp[idx][target + 20030] = 1; return  dp[idx][target + 20030];}
        
        // nums中的数组经过前面元素选择正负号后 当前最后元素等于 -target 的方案数 也就是选择-号 方案数为1
        if (idx == 0 && target == -val) { dp[idx][target + 20030] = 1; return  dp[idx][target + 20030]; }
        
        //如果当前不是最后一个元素 搜索当前元素选择+ 和-  能最后得到target的方案数之和就是本函数的答案 记录进数组中
        dp[idx][target + 20030] = solve(nums, target - val, idx - 1) + solve(nums, target + val, idx - 1);

        return dp[idx][target+ 20030];
    }

    int findTargetSumWays(vector<int>& nums, int target) {
        dp.resize(22, vector<int>(40030, -1));
        int n = nums.size()-1;
        solve(nums, target,n);

        return dp[n][target+ 20030];
    }
};

我的视频题解空间

posted on 2022-05-01 13:10  itdef  阅读(61)  评论(0编辑  收藏  举报

导航