每日一题:LeetCode之目标和

给定一个非负整数数组,a1, a2, …, an, 和一个目标数,S。现在你有两个符号 + 和 -。对于数组中的任意一个整数,你都可以从 + 或 -中选择一个符号添加在前面。

返回可以使最终数组和为目标数 S 的所有添加符号的方法数。

示例 1:

输入: nums: [1, 1, 1, 1, 1], S: 3
输出: 5
解释:

-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

一共有5种方法让最终目标和为3。

思路: 暴力递归和01背包(动态规划)

暴力递归
对于每一个数nums[i]只有两种操作,要么加,要么减,当遍历完数组并且与目标和相等,即找到一个符合组合。

 public int findTargetSumWays(int[] nums, int S) {
        return find(nums,S,0,0);
    }
    public int find(int[] nums,int S,int sum,int index){
        if(index==nums.length&&S==sum){
            return 1;
        }

        if(index==nums.length)
            return 0;

        return (find(nums,S,sum+nums[index],index+1)+find(nums,S,sum-nums[index],index+1));
    }

0-1背包:
难点在将原问题转换成01背包,将原问题转换为由正数和x和负数和y组成,x,y分别为和的绝对值

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

这样我们就找数组中满足和为x的组合,典型的0-1背包问题(这个数装还是不装,总容量为X)
我们使用动态规划解决这个问题,设dp[i][j]表示前i个数和为j的次数。状态方程如下。
当前的数可以选时:

dp[i][j]=dp[i-1][j]+dp[i-1][j-nums[i]]]

当前的数不能选时:

dp[i][j]=dp[i-1][j]
public int findTargetSumWays(int[] nums, int S) {
        int sum=0;
        for(int i=0;i<nums.length;i++)
            sum+=nums[i];
        
        if((S+sum)%2==1||S>sum)return 0;

        int target=(S+sum)/2;
        int[][] dp=new int[nums.length+1][target+1];

        for(int i=0;i<=nums.length;i++)
            dp[i][0]=1;

        for(int i=1;i<=nums.length;i++)
            for(int j=0;j<=target;j++){
                if(nums[i-1]<=j)dp[i][j]=dp[i-1][j]+dp[i-1][j-nums[i-1]];
                else
                    dp[i][j]=dp[i-1][j];
            }        
        return dp[nums.length][target];
    }
发布了18 篇原创文章 · 获赞 0 · 访问量 150

猜你喜欢

转载自blog.csdn.net/qq_40053995/article/details/104750349
今日推荐