1.一件物品只有两种选的情况
2.每件物品可以选无限件
3.每件物品可以选的不同
4.
5.两种限制
6.每组只能选一件
7.求方案数
8.记背包问题最优的怎么选的方案
9.选一件物品,要选依赖性的,有一定的限制
1.01背包问题
每件物品只能用一次
体积从大到小枚举
dp类似类似递推问题(如斐波那契数列),找递推公式
暴力做,dfs,2的N次方,用dp优化
朴素版(二维dp)
有限集合的最值问题
1.状态表示
1.描述的集合是什么(所以之考虑前i个物品并且总体积不超过j的选法的集合)
每个条件代表一个维度f[i][j]
2.属性:f[i][j]的值(集合每个方案的属性,题目要求的)
2.状态的计算
1.集合的划分,找最后一个不同点
1.所有不选第i个物品的方案
所有从1~i-1个物品里选择
f[i][j]==f[i-1][j]
2.所有选第i个物品的方案(最后必然包括物品i)
j>=v[i] 变化的部分+不变的部分
j<v[i] 是空的,不放第i个物品
f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i])注意初始化是最开始的特殊情况,什么都不考虑的情况下,或者是数列里的a1
优化
从空间,时间上
二维到一维
f[i]这一层只用到上一层f[i-1],要么是用自己要么是用比自己小的
for(int i=1;i<=n;i++)
for(int j=vs;j>=v[i];j--)
f[i][j]=max(f[j],f[j-v[i]]+w[i])
j从大到小保证j-v[i]是在第i-1层更新的,不是第i层
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1010;
int v[N],w[N];
int f[N];
//因为用的i,j都是一侧的不是两侧的,所以可以用一维计算
int n,vs;
int main(){
scanf("%d%d",&n,&vs);
for(int i=1;i<=n;i++){
scanf("%d%d",&v[i],&w[i]);
}
for(int i=1;i<=n;i++){
for(int j=vs;j>=v[i];j--){
f[j]=max(f[j],f[j-v[i]]+w[i]);
}
}
printf("%d\n",f[vs]);
return 0;
}
2.完全背包
每件物品可以用多次
体积从小到大循环
循环正向是因为,f[i][j-v]可以已经被更新过(里面已经包含了若干第i件物品)
证明可以用数学归纳法
体积循环一维空间有限制,二维空间无限制
//f[i][j]//i可以选i种物品,j当前背包的容量
#include<iostream>
using namespace std;
const int N=1010;
int n,m;
int v[N],w[N];
int f[N];
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>v[i]>>w[i];
for(int i=1;i<=n;i++)
{
//朴素版
for(int j=v[i];j<=m;j++){
// for(int k=0;k*v[i]<=j;k++){
// f[i][j]=max(f[i][j],f[i-1][j-k*v[i]]+k*w[i]);
// }
//优化n*n
// f[i][j]=f[i-1][j];
// if(j>=v[i])f[i][j]=max(f[i-1][j],f[i][j-v[i]]+w[i]);
//优化空间n
f[j]=max(f[j],f[j-v[i]]+w[i]);
//01背包从大到小循环,因为01背包需要的是第i-1层的状态,但是到f[i][j]时前面的第i-1层已经被更新为第i层
}
}
cout<<f[m]<<endl;
return 0;
}
??????初始化,哔哩哔哩证明没懂,正好是f[m],则初始化要f[0],f[]=负无穷
不用正好f[m],初始化不止把f[0]=0,所有f[i]=0,转移最开始不一定是从f[0]转移过来,可以从任意一个数转移过来,当用不完背包总容量,还剩k个,就可以从f[k]转移,那一定可以和最优解一样的体积,
3.多重背包问题
每件物品有个数限制