Skip to content
本页目录

0494-目标和

https://leetcode.cn/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. 定义 dp[i,j] 表示从 0-i 中选出一些数,组成为j的最多种方法
  2. 状态转移方程 dp[i,j] = dp[i-1, j-nums[i]] + dp[i-1,j+nums[i]]
  3. 初始化 dp[0,j] 当 j = nums[0] 时, dp[0,j] = 1

参考代码

csharp
public class Solution {
    public int FindTargetSumWays(int[] nums, int target) {
    	int sum = 0;
    	for(int i=0; i<nums.Length; i++){
    		sum+=nums[i];
    	}

    	int[,] dp = new int[nums.Length,sum+1];
    	//设置初始值,表示
    	dp[0,nums[0]] = 1;
    	//状态转移
    	for(int i=0; i<nums.Length; i++){
    		for(int j=0; j<=sum; j++){
    			if(j>=nums[i]){
    				dp[i,j] += dp[i-1,j-nums[i]];
    			}
    			if(j+nums[i] <= sum){
    			 	dp[i,j] += dp[i-1,j+nums[i]];
    			}
    		}
    	}

    	return dp[nums.Length-1,target];
    }
}

参考代码2:考虑负数区间的情况

注意dp数组循环的时候, j 从 -sum 然后到 sum ,并且计算 j+sum的值

csharp
public class Solution{
	public int FindTargetSumWays(int[] nums, int target){
		int sum = 0;
		for(int i=0; i<nums.Length; i++){
			sum+=nums[i];
		}
        int range = sum*2+1;
		int[,] dp = new int[nums.Length, range];
		//设置初始值
        //Console.WriteLine("range={0},sum={1}",range,sum);
		dp[0,sum+nums[0]] += 1;
		dp[0,sum-nums[0]] += 1; //如果nums[0] == 0 继续累积
		//状态转移
		for(int i=1; i<nums.Length; i++){
			for(int j=-sum;j<=sum;j++){

				if(j+sum >= nums[i]){
					dp[i,j+sum] += dp[i-1,j+sum-nums[i]];
				}

				if(j+sum+nums[i] < range){
					dp[i,j+sum] += dp[i-1,j+sum+nums[i]];
				}
			}
		}
		//返回结果
        if(target+sum < range && target+sum >=0){
            return dp[nums.Length-1,target+sum];
        }
        else{
            return 0;
        }
	}
}

思路2:转换为01背包:思路巧妙

https://leetcode.cn/problems/target-sum/solution/by-mountain-ocean-v4g1/

目标和 target 总和是 sum, 加号组成的和是 x, x = (sum+target)/2 问题转化为,在nums中选择n个数,和组成x的方法数

参考代码

csharp
public class Solution{
	public int FindTargetSumWays(int[] nums, int target){
		int sum = 0;
		for(int i=0; i<nums.Length; i++){
			sum+=nums[i];
		}
        if(Math.Abs(target) > sum){
            return 0;
        }
        if( (sum + target) % 2 == 1){
            return 0;
        }
		int x = (sum+target)/2;
		int[] dp = new int[x+1];
		//01背包问题
		//dp[j]表示0~i中选择的数,凑成j的方法
        //初始化 nums[0]
        dp[0] = 1;
        for(int i=0; i<nums.Length; i++){
            for(int j=x; j>=0; j--){
                if(j>=nums[i]){
                    //不选自己,或者选择自己
                    dp[j] = dp[j] + dp[j-nums[i]];
                }
            }
        }
        return dp[x];
	}
}

思路3:回溯法

csharp
public class Solution {
    int count = 0;
    public void dfs(int[] nums, int target, int index, int sum) {
        if (index == nums.Length) {
        	//回溯到最后一位,如果 sum == target 则 count++
            if (sum == target) {
                count++;
            }
        } else {
            dfs(nums, target, index + 1, sum + nums[index]);
            dfs(nums, target, index + 1, sum - nums[index]);
        }
    }

    public int FindTargetSumWays(int[] nums, int target) {
        dfs(nums, target, 0, 0);
        return count;
    }
}

复习回溯:20220515

csharp
public class Solution {
    int count;
    private void dfs(int[] nums, int begin, int target){
        if(begin == nums.Length){
            if(target == 0){
                count++;
            }
            return;
        }
        dfs(nums,begin+1,target-nums[begin]);
        dfs(nums,begin+1,target+nums[begin]);
    }

    public int FindTargetSumWays(int[] nums, int target) {
        //使用 dfs 
        dfs(nums,0,target);
        return count;
    }
}

复习01背包

csharp
public class Solution {
    public int FindTargetSumWays(int[] nums, int target) {
        // 转为为01背包问题
        // 假设转换完成后会有两个部分,整数部分和负数部分 sumA + sumB = target
        // sumA 为整数部分 subB 为负数部分,我们要找寻等式成立的情况
        // 因为原来数值总和是 sum 所以就有 sumA - sumB = sum , 等于把负数的 sumB 都变成正号
        // 两个等式合并 有 2 * sumA = (target + sum), sumA = (target + sum) / 2;
        // 所以就转化为从nums中选择和为 sumA 的方法数,01背包问题

        int sum = nums.Sum();
        
        // 越界判断,如果 target > sum 或者 小于 -sum 说明肯定凑不起来
        // 不能正好整除为2,说明也凑不起来
        if(target > sum || target < -sum || (sum + target) % 2 == 1){
            return 0;
        }

        int sumA = (target + sum) / 2;
        int[] dp = new int[sumA+1]; //dp[j]表示选择数字能组成和j的方案数
        dp[0] = 1; //初始化,组成0的方案数是1
        for(int i=0; i<nums.Length; i++){
            for(int j=sumA; j>=0; j--){
                if(j >= nums[i]){
                    dp[j] = dp[j] + dp[j-nums[i]];
                }
            }
        }
        return dp[sumA];
    }
}

复习回溯:20220629

csharp
public class Solution {
    //思路1:回溯法,数据集比较小,可以尝试回溯法
    int count = 0;
    private void dfs(int[] nums, int begin, int target){
        if(begin == nums.Length){
            if(target == 0){
                count++;
            }
            return;
        }
        dfs(nums,begin+1,target-nums[begin]); //符号在此处体现
        dfs(nums,begin+1,target+nums[begin]);
    }

    public int FindTargetSumWays(int[] nums, int target) {
        dfs(nums,0,target); 
        return count;
    }
}

复习01背包:20220629

注意越界判断

csharp
public class Solution {
    public int FindTargetSumWays(int[] nums, int target) {
        //添加正负号之后,和分为两部分,正数部分和负数部分 sumA + sumB = target
        //原先的和 sum = sumA - sumB
        //合并的 sumA = (sum + target) / 2
        //问题转为01背包,即从数组中选择数组凑成 sumA 的方法数

        int sum = nums.Sum();

        // 越界判断,如果 target > sum 或者 小于 -sum 说明肯定凑不起来
        // 不能正好整除为2,说明也凑不起来
        if(target > sum || target < -sum || (sum + target) % 2 == 1){
            return 0;
        }

        int sumA = (sum + target) / 2;
        int[] dp = new int[sumA+1];
        dp[0] = 1; //从中间选择0个数可以凑成0,方案数为1

        for(int i=0; i<nums.Length; i++){ //遍历物品
            for(int j=sumA; j>=0; j--){
                if(j >= nums[i]){
                    dp[j] = dp[j] + dp[j-nums[i]];
                }
            }
        }
        return dp[sumA];

    }
}

Released under the MIT License.