1.动态规划二维dp数组
- dp[i][j]表示[0,i]的元素装满大小为j的背包有多少种方法。
- 递推公式:dp[i][j]=dp[i-1][j]+dp[i-1][j-nums[i]],
- 当当前物品大于当前背包的大小时,放满大小为j的背包的方法数仍就为dp[i-1][j].
- 当当前物品小于等于当前背包的大小时,放满大小为j的背包的方法数=dp[i-1][j]+dp[i-1][j-nums[i]],其中dp[i-1][j-nums[i]]是增加了物品nums[i]后增加的方法数。
- 初始化:初始化第一列,其中特别的:放满背包大小0 有多少种方法——要么什么也不放,要么放入大小为0的物品。初始化时要根据问题,具体分析。
- 遍历:由于二维数组保留了两个维度所有值,所以先便利包还是先遍历物品都可以
public int findTargetSumWays(int[] nums, int target) {
int sum=0;
for(int num:nums){
sum+=num;
}
//是否能按照需求分成两部分
if((sum+target)%2!=0) return 0;
if((sum-target)%2!=0) return 0;
//把所有值当作整数,分成两部分一正一负即可,所以如果target总保持为正数
if(target<0) target=-target;
int size= (int)Math.ceil((float)(sum+target)/2);
int dp[][]=new int[nums.length][size+1];
//初始化
for(int[] temp:dp) Arrays.fill(temp,0);
int countZero=0;
for(int i=0;i<nums.length;i++){
if(nums[i]==0) countZero++;
dp[i][0]=countZero+1;
}
for(int j=1;j<=size;j++){
if(nums[0]==j) dp[0][j]=1;
else dp[0][j]=0;
}
//遍历顺序
for(int i=1;i< nums.length;i++){
for(int j=0;j<=size;j++){
if(j<nums[i]){
dp[i][j]=dp[i-1][j];
}else{
dp[i][j]=dp[i-1][j]+dp[i-1][j - nums[i]];
}
}
}
return dp[nums.length-1][size];
}

2.一维滚动数组——存储压缩
- dp[j]表示装满大小为j的背包有多少种方式。
- 递推公式:dp[j]=max(dp[j],dp[j-weight[i]]+values[i])
- 初始化:右边的值总是由最左边的值推导而来,dp[0]表示使背包价值为0有多少种放置方法——要么什么也不放,要么放大小为0的物品。
- 遍历:由于以为滚动数组是二维dp数组的动态行滚动更新,所以遍历顺序总是先物品后背包。
- 注意:为了防止用同层修改过的值修改本行其他值,导致物体重复放置,故采用倒序遍历背包。
public int findTargetSumWays(int[] nums, int target) {
int sum=0;
for(int num:nums) sum+=num;
if((sum+target)%2!=0) return 0;
if((sum-target)%2!=0) return 0;
if(target<0) target=-target;
int size= (int)Math.ceil((float)(sum+target)/2);
int dp[]=new int[size+1];
//初始化
Arrays.fill(dp,0);
dp[0]=1;
//遍历顺序
for(int i=0;i< nums.length;i++){
for(int j=size;j>=nums[i];j--){
dp[j] += dp[j - nums[i]];
}
}
return dp[size];
}

3.分析