背包
线性dp
区间dp
重点:状态转移
出发点:
1. 状态表示(几维)例如 f(i,j)
a.集合
- 所有选法
- 条件
b.属性 Max、Min、数量
2. 状态计算(如何一步一步计算出每一步)
集合划分
原则:不重、不漏
dp优化:对代码或者方程进行等价变形
n个物品,容量V的背包,每个物品v体积、w价值
n个物品,容量V的背包,每个物品v体积、w价值
特点:每件物品最多用一次
所有选法满足1.只从前i个物品中选2.总体积<=j的集合
答案:f(n,v)
集合划分:
f(i,j) -> 不含i f(i-1,j)
-> 含i f(i-1,j-v[i]) + w[i]
f(i,j) 集合的属性:这里指集合里面所有选法的最大值
f(i,j) = max(f(i-1,j),f(i-1,j-v[i])+w[i])
模板:
二维:
for(int i=1;i<=n;i++){
for(int j=0;j<=m;j++){
f[i][j]=f[i-1][j];
if(j>=v[i]) f[i][j]=max(f[i][j],f[i-1][j]-v[i]+w[i]);
}
}
cout<<f[n][m]<<endl;
一维: 滚动数组
for(int i=1;i<=n;i++){
for(int j=m;j>=v[i];j--){
f[j]=max(f[j],f[j-v[i]]+w[i]);
}
}
cout<<f[m]<<endl;
n个物品,容量V的背包,每个物品v体积、w价值
特点:每件物品可以用无限次
分析:
状态表示f(i,j)
集合:所有只考虑前i个物品,且总体积不大于j的所有选法
属性:Max
1.去掉k个物品i
2.求Max,f(i-1,j-k*v[i])
3.再加回来k个物品i f(i-1,j-k*v[i])+k*w[i]
动态转移方程:f(i,j)=f(i-1,j-k*v[i])+k*w[i]
状态计算 集合的划分
模板:
朴素版
for(int i=1;i<=n;i++){
for(int j=0;j<=m;j++){
for(int k=0;k*v[i]<=j;k++){
f[i][j]=max(f[i][j],f[i-1][j-v[i]*k]+w[i]*k);
}
}
}
cout<<f[n][m]<<endl;
优化版
for(int i=1;i<=n;i++){
for(int j=0;j<=m;j++){
f[i][j]=f[i-1][j];
if(j>=v[i]) f[i][j]=max(f[i][j],f[i][j-v[i]]+w[i]);
}
}
cout<<f[n][m]<<endl;
一维:
for(int i=1;i<=n;i++){
for(int j=v[i];j<=m;j++){
f[j]=max(f[j],f[j-v[i]]+w[i]);
}
}
cout<<[m]<<endl;
优化推算过程:
01背包(上)与完全背包(下) 状态转移方程比较 :
n个物品,容量V的背包,每个物品v体积、w价值
特点:每个物品有si个
状态表示f[i,j]:
集合: 所有只从前i个物品中选,并且总体积不超过j的选法
属性:最大值
动态转移方程:f[i][j]=max(f[i][j],f[i-1][j-v[i]*k]+w[i]*k); k=0,1,2,...,s[i]
状态计算
优化
二进制优化方式 O(nvs)–>O(nlogvs)
打包分组
例: s = 200
1,2,4,8,16,32,64,73
s
1,2,4,8,…,2^k,c ( c < 2 ^(k+1) )
模板:
暴力写法:
for(int i=1;i<=n;i++){
for(int j=0;j<=m;j++){
for(int i=k;k<=s[i]&&k*v[i]<=j;k++){
f[i][j]=max(f[i][j],f[i-1][j-v[i]*k]+w[i]*k);
}
}
}
cout<<f[n][m]<<endl;
二进制优化
int cnt=0;
for(int i=1;i<=n;i++){
int a,b,s;
cin>>a>>b>>s;
int k=1;
while(k<=s){
cnt++;
v[cnt]=a*k;
w[cnt]=b*k;
s-=k;
k*=2;
}
if(s>0){
cnt++;
v[cnt]=a*s;
w[cnt]=b*s;
}
}
n=cnt;
for(int i=1;i<=n;i++){
for(int j=m;j>=v[i];j--){
f[j]=max(f[j],f[j-v[i]]+w[i]);
}
}
cout<<f[m]<<endl;
n个物品,容量V的背包,每个物品v体积、w价值
特点:物品有n组,有若干种物品,每一组最多选一种
状态表示f[i,j]
集合:只从前i组物品中选,且总体积不大于j的所有选法
属性:Max
状态计算 :集合划分
模板
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>s[i];
for(int j=0;j<s[i];j++){
cin>>v[i][j]>>w[i][j];
}
}
for(int i=1;i<=n;i++){
for(int j=m;j>=0;j--){
for(int k=0;k<s[i];k++){
if(v[i][k]<=j){
f[j]=max(f[j],f[j-v[i][k]]+w[i][k]);
}
}
}
}
cout<<f[m]<<endl;