0-1背包:
0-1背包(滚动数组优化)
c++
python:
完全背包:
完全背包——恰好装满求最小值
多重背包:
多重背包(转化成0-1背包,再二进制分解优化)第二层循环要从大到小
二进制拆解即:7 = 2^0+2^1+2^2
多重背包 (单调队列优化)难!
0-1背包求最优方案数:https://www.acwing.com/problem/content/11/
求方案数,转化为01背包:
https://leetcode-cn.com/problems/target-sum/
传统回溯做法:
class Solution {
private int tt,ans,n;
public void dfs(int[] nums,int summ,int i){
if(i==n-1){
if(summ==tt){
ans++;
}
return ;
}
dfs(nums,summ+nums[i+1],i+1);
dfs(nums,summ-nums[i+1],i+1);
}
public int findTargetSumWays(int[] nums, int target) {
tt = target;
n = nums.length;
dfs(nums,nums[0],0);
dfs(nums,-nums[0],0);
return ans;
}
}
记数组的元素和为sum,添加- 号的元素之和为neg,则其余添加+ 的元素之和为sum−neg,得到的表达式的结果为
(sum−neg)−neg=sum−2⋅neg=target
即
neg = (sum-target)/2
由于数组 nums 中的元素都是非负整数,neg 也必须是非负整数,所以上式成立的前提是 sum−target 是非负偶数。若不符合该条件可直接返回 0。
若上式成立,问题转化成在数组 nums 中选取若干元素,使得这些元素之和等于 neg,计算选取元素的方案数。我们可以使用动态规划的方法求解。
定义二维数组 dp,其中 dp[i][j] 表示在数组 nums 的前 i 个数中选取元素,使得这些元素之和等于 j 的方案数。假设数组 nums 的长度为 n,则最终答案为 dp[n][neg]。
当没有任何元素可以选取时,元素和只能是 0,对应的方案数是 1,因此动态规划的边界条件是:
dp[0][j] = 1,j=0 ;dp[0][j]=0,j>=1;
当 1≤i≤n 时,对于数组nums 中的第 ii 个元素 num(i的计数从 1 开始),遍历 0≤j≤neg,计算 dp[i][j] 的值:
如果j<num,则不能选 num,此时有dp[i][j]=dp[i−1][j];
如果j≥num,则如果不选 num,方案数是dp[i−1][j],如果选num,方案数是dp[i−1][j−num],此时有 dp[i][j]=dp[i−1][j]+dp[i−1][j−num]。
因此状态转移方程如下:
dp[i][j]={
dp[i−1][j],j<nums[i]
dp[i−1][j]+dp[i−1][j−nums[i]],j≥nums[i]
}
最终得到 dp[n][neg] 的值即为答案。
class Solution {
public int findTargetSumWays(int[] nums, int target) {
int sum=0,neg=0;
int n = nums.length;
for(int i=0;i<n;i++){
sum+=nums[i];
}
int diff = sum-target;
neg = diff/2;
if(diff%2!=0||diff<0){
return 0;
}
//neg = (sum-target)/2;
int[][] dp = new int[n+1][neg+1];
dp[0][0]=1;
for(int i=1;i<=n;i++){
for(int j=0;j<=neg;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[n][neg];
}
}