> 文档中心 > 【动态规划01背包】leetcode 494.目标和

【动态规划01背包】leetcode 494.目标和

文章目录

  • 二维数组版本
  • 二维数组升级版
  • 一维数组版

image.png
这道题其实是一个01背包问题,只是加了一点的小变形而已
现在考虑:
怎么可以将这个变为从n个数中挑选出target个重量的组合数

可以将数分为两组,一组是符号是+的为left,一组是符号为-的为right
sum=left+right
target=left-right
所以,我们真正的target就是left=(sum+target)/2,我们要做的就是从上面的数组中挑选出满足加起来的重量是target的组合数

二维数组版本

class Solution {    public int findTargetSumWays(int[] nums, int target) { if(nums==null||nums.length==0)     return 0; int n=nums.length; //下面是计算出真正的target: int sum=0; for(int i=0;i<n;i++){     sum+=nums[i]; } //特殊情况: if(target>sum||target<-1*sum||(target+sum)%2==1)     return 0; target=(target+sum)/2; int[][] f=new int[n+1][target+1]; f[0][0]=1; for(int i=1;i<=target;i++){     f[0][i]=0; } for(int i=1;i<=n;i++){     for(int j=0;j<=target;j++){  f[i][j]=f[i-1][j];  if(j>=nums[i-1])      f[i][j]+=f[i-1][j-nums[i-1]];     } } return f[n][target];    }}

二维数组升级版

上面的空间复杂度是O(MN)
但是我们还可以进行简化成f[2][N]

我们观察到f[i][j]只是和它的上一层的f[i-1][j]和f[i-1][j-nums[i-1]]有关
所以说我们就可以只开辟一个2*n的数组进行滚动就可以了

这里我们使用old和new1来表示上一层和本层,这样就简化了时间复杂度

class Solution {    public int findTargetSumWays(int[] nums, int target) { if(nums==null||nums.length==0)     return 0; int n=nums.length; //下面是计算出真正的target: int sum=0; for(int i=0;i<n;i++){     sum+=nums[i]; } //特殊情况: if(target>sum||target<-1*sum||(target+sum)%2==1)     return 0; target=(target+sum)/2; int[][] f=new int[2][target+1]; f[0][0]=1; for(int i=1;i<=target;i++){     f[0][i]=0; } int old=0,new1=0; for(int i=1;i<=n;i++){     old=new1;     new1=1-old;     for(int j=0;j<=target;j++){  f[new1][j]=f[old][j];  if(j>=nums[i-1])      f[new1][j]+=f[old][j-nums[i-1]];     } } return f[new1][target];    }}

一维数组版

上面的代码简化了不少,但是还可以更加的简洁,我们看一下转移方程:

f[new1][j]=f[old][j];if(j>=nums[i-1])    f[new1][j]+=f[old][j-nums[i-1]];

我们可以不必开两层数组,直接在上一层old中计算本层new1的值,
从后往前计算,因为f[old][j-nums[i-1]];我们需要用到的还是old的数据,所以为了不破坏,我们应该使用从后往前算.

class Solution {    public int findTargetSumWays(int[] nums, int target) { if(nums==null||nums.length==0)     return 0; int n=nums.length; //下面是计算出真正的target: int sum=0; for(int i=0;i<n;i++){     sum+=nums[i]; } //特殊情况: if(target>sum||target<-1*sum||(target+sum)%2==1)     return 0; target=(target+sum)/2; int[] f=new int[target+1]; //这里就相当于i=0,前0个数字 //前0个数字组成重量0可以有1种方法 f[0]=1; //前0个数字不可以组成重量i for(int i=1;i<=target;i++){     f[i]=0; } //遍历1~n个数字可以组成的重量 for(int i=1;i<=n;i++){     for(int j=target;j>=0;j--){  if(j>=nums[i-1])      f[j]+=f[j-nums[i-1]];     } } return f[target];    }}