列表

详情


NC243. 目标和

描述

给定一个整数数组nums和一个整数target,请你返回该数组能构成多少种不同的表达式等于target。
规则如下:
1.将数组里每个整数前面可以添加"+"或者"-"符号,组成一个表达式,例如[1,2],可以变成”+1+2","+1-2","-1+2","-1-2",这四种
2.只能添加"+"与"-"符号,不能添加其他的符号
3.如果构不成等于target的表达式,请返回0
4.保证返回的结果个数在整数范围内

数据范围:





示例1

输入:

[1,1,1,2],3

输出:

3

说明:

-1 + 1 + 1 + 2 = 3
+1 - 1 + 1 + 2 = 3
+1 + 1 - 1 + 2 = 3

示例2

输入:

[2],2

输出:

1

原站题解

上次编辑到这里,代码来自缓存 点击恢复默认模板

C++ 解法, 执行用时: 2ms, 内存消耗: 460KB, 提交时间: 2022-01-22

class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param nums int整型vector 
     * @param target int整型 
     * @return int整型
     */
    int findTargetSumWays(vector<int>& nums, int target) {
        // write code here
        if(!nums.size()) return 0;
        int sum=0;
        for(auto& num:nums)
            sum+=num;
        if((sum+target)%2) return 0;
        target=(sum+target)/2;
        vector<int> dp(target+1);
        dp[0]=1;
        for(int i=0;i<nums.size();i++){
            for(int j=target;j>=nums[i];j--)
                dp[j]+=dp[j-nums[i]];
        }
        return dp[target];
    }
};

C++ 解法, 执行用时: 3ms, 内存消耗: 408KB, 提交时间: 2022-05-18

class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param nums int整型vector 
     * @param target int整型 
     * @return int整型
     */
    int findTargetSumWays(vector<int>& nums, int target) {
        // write code here
        if(nums.size() == 0)
            return 0;
        int sum = 0;
        for(auto num: nums)
            sum+=num;
        if((sum+target)%2==1)
            return 0;
        
        int v = (sum+target)/2;
        vector<vector<int>> dp(nums.size()+1,vector<int>(v+1,0));
        dp[0][0] = 1;
        for(int i =1; i<=nums.size(); ++i){
            for(int j =0; j<=v; ++j){
                dp[i][j] = dp[i-1][j];
                if(j>=nums[i-1])
                    dp[i][j] +=  dp[i-1][j-nums[i-1]];
            }
        }
        return dp[nums.size()][v];
    }
};

C++ 解法, 执行用时: 3ms, 内存消耗: 420KB, 提交时间: 2022-05-24

class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param nums int整型vector 
     * @param target int整型 
     * @return int整型
     */
    int findTargetSumWays(vector<int>& nums, int target) {
        // write code here
        int sum=0;
        if(nums.empty()) return 0;
        for(auto n:nums) sum+=n;
        int diff = sum-target;
        
        if(diff%2 || diff<0) return 0;
        vector<int> f(diff/2+1);
        
        int neg = diff/2;
        
        f[0]=1;
        
        int n = nums.size();
        for(int i=0;i<n;i++){
            for(int j=neg;j>=nums[i];j--){
                f[j]+=f[j-nums[i]];
            }
        }
        return f[neg];
    }
};

C++ 解法, 执行用时: 3ms, 内存消耗: 420KB, 提交时间: 2022-01-25

class Solution {
public:
    /*
    方法一:暴力递归
    每一层递归可以选择对当前数添加或不添加负号,将其累加到结果上,直到凑出target
    */
    int findTargetSumWays1(vector<int>& nums, int target) {
        if (nums.size() == 0) return 0;
        //return dfs_v1(nums, target, 0);
        int res = 0; //保存结果
        dfs_v2(nums, target, 0, 0, res);
        return res;
    }
    
    /*
    方法二:记忆化搜索(避免重复计算)
    */
    int findTargetSumWays2(vector<int>& nums, int target) {
        if (nums.size() == 0) return 0;
        int n = nums.size();
        map<int, map<int, int>> mem;
        return dfs_mem(nums, target, mem, 0);
    }
    
    /*
    方法三:动态规划 - 有BUG!
    
    解题思路
    本题可以转化为一个0-1背包问题。
    我们将前面添加号的分为一组,记其累加和为a,将前面添减号的分为一组,记其累加和为b。
    如果能构成一个表达式,使得结果为target,则有a+b=sum,a−b=target,其中sum为数组中所有数字的累加和。
    继续化简一下,可以得到:a=(sum+target)/2。
    如果数组中存在若干数字之和等于a,则对应其中一个合法的表达式等于target。
    要想a存在,sum+target必为偶数。
    
    状态定义:dp[i][j]dp[i][j]dp[i][j]表示i个元素时,有多少种不同的组合,其累加和为j。
    状态初始化:初始化组成0的情况数为1。
    状态转移:遍历nums数组中每一个数字,默认不选当前数字,即dp[i+1][j]=dp[i][j]。
    如果j大于当前数字,则需要加上j−nums[i]时的组合数,即dp[i+1][j]+=dp[i][j−nums[i]]。
    
    复杂度分析
    时间复杂度:总共两次循环,需要执行n∗(V+1)次,所以时间复杂度是O(n∗V)。
    空间复杂度:需要额外大小为(n+1)∗(V+1)的dp数组,所以空间复杂度为O(n∗V)。
    
    参考:
    https://blog.nowcoder.net/n/150a9cc1034148078a5686efed7158b4
    */
    int findTargetSumWays(vector<int>& nums, int target) {
        //边界情况判断
        int n = nums.size();
        if(n == 0) return 0;
        //记录累加和
        int sum = 0;
        //遍历nums数组
        for(int num:nums){
            sum += num;
        }
        //计算背包容量
        int V = (sum + target) / 2;
        //如果为奇数,说明nums数组中找不打和为(sum+target)/2的若干数字
        if((sum + target) % 2 == 1) return 0;
        
        //dp[i][j]表示i个元素时,有多少种不同的组合,其累加和为j
        //int dp[n+1][V+1];
        vector<vector<int>> dp(n+1, vector<int>(V+1));
        //初始化
        dp[0][0]=1;
        for(int i=0; i<n; i++){
            for(int j=0; j<=V; j++){
                //默认不选当前数字
                dp[i+1][j] = dp[i][j];
                //如果选择当前数字,则需要加上j-nums[i]时的组合数
                if(j >= nums[i]){
                    dp[i+1][j] += dp[i][j-nums[i]];
                }
            }
        }
        return dp[n][V];
    }
    
    /*
    动态规划解法优化
    
    空间优化思路
    由于方法一中,每次状态转移,只与上一行的状态有关,所以可以只使用一个维度构建dp数组。
    每个数字只能选一次,所以需要倒序遍历背包容量,避免重复计算。
    */
    int findTargetSumWays3(vector<int>& nums, int target) {
        //边界情况判断
        int n = nums.size();
        if(n == 0) return 0;
        //记录累加和
        int sum = 0;
        //遍历nums数组
        for(int num:nums){
            sum += num;
        }
        //计算背包容量
        int V = (sum + target) / 2;
        //如果为奇数,说明nums数组中找不打和为(sum+target)/2的若干数字
        if((sum + target) % 2 == 1) return 0;
        
        //dp[j]表示有多少种不同的组合,其累加和为j
        vector<int> dp(V+1);
        //初始化
        dp[0]=1;
        for(int i=0; i<nums.size(); i++){
            //每个数字只选一次,所以需要倒序遍历,避免重复
            for(int j=V; j>=nums[i]; j--){
                dp[j] += dp[j-nums[i]];
            }
        }
        return dp[V];
    }
    
private:
    //暴力递归调用
    int dfs_v1(vector<int> nums, int rest, int index) {
        // 到最后一个数如果rest为0了,就形成了一种合法的方案
        if (index == nums.size()) {
            if (rest == 0) return 1;
            else return 0;
        }
        // 否则就给当前的数加上负号或不加负号累加到rest上,继续尝试下面的数
        return dfs_v1(nums, rest + nums[index], index + 1) + dfs_v1(nums, rest - nums[index], index + 1);
    }
    
    void dfs_v2(vector<int> nums, int target, int index, int sum, int &count) {
        if (index == nums.size() && sum == target) {
            count += 1;
            return;
        }
        if (index == nums.size()) return;
        dfs_v2(nums, target, index + 1, sum + nums[index], count);
        dfs_v2(nums, target, index + 1, sum - nums[index], count);
    }
    
    //记忆化搜索递归调用
    int dfs_mem(vector<int> nums, int rest, map<int, map<int, int>> mem, int index) {
        if (mem.find(index) != mem.end() && mem[index].find(rest) != mem[index].end()) {
            mem[index][rest]; // 缓存命中,直接返回
        }
        if (index == nums.size()) // 到最后一个数如果rest为0了,就形成了一种合法的方案
            return rest == 0 ? 1 : 0;
        map<int, int> map;
        // 否则就给当前的数加上负号或不加负号累加到rest上,继续尝试下面的数
        int res = dfs_mem(nums, rest + nums[index], mem, index + 1) + dfs_mem(nums, rest - nums[index], mem, index + 1);
        map[rest] = res;
        mem[index] = map;
        return res;
    }
};

C++ 解法, 执行用时: 3ms, 内存消耗: 424KB, 提交时间: 2022-04-01

class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param nums int整型vector 
     * @param target int整型 
     * @return int整型
     */
    int findTargetSumWays(vector<int>& nums, int target) {
        // write code here
        if(nums.size()==0) return 0;
        int sum=0;
        for(int i:nums){
            sum+=i;
        }
        if((sum+target)%2==1) return 0;
        if(sum<abs(target)) return 0;
         target=(sum+target)/2;
        vector<int> dp(target+1,0);
        dp[0]=1;
        for(int i=0;i<nums.size();i++){
            for(int j=target;j>=nums[i];j--){
                dp[j]+=dp[j-nums[i]];
            }
        }
        return dp[target];
    }
};

上一题