基础dp
背包
- 01背包
for (int i = 1; i <= n; i++)
for (int j = V; j >= c[i]; j--)
f[j] = max(f[j], f[j - c[i]] + w[i]);
初始化细节:
要求恰好装满背包:初始化时
f
[
0
]
f[0]
f[0]为0,
f
[
1...
V
]
=
−
∞
f[1...V]=-\infty
f[1...V]=−∞
不要求把背包装满:初始化时
f
[
0...
V
]
=
0
f[0...V]=0
f[0...V]=0
体积为负的处理方法
- 完全背包
for (int i = 1; i <= n; i++)
for (int j = c[i]; j <= V; j++)
f[j] = max(f[j], f[j - c[i]] + w[i]);
- 多重背包
假设第 i i i种物品有 s i s_i si个,那么把 s i s_i si拆成 1 , 2 , 4 , … , 2 k − 1 , n − 2 k + 1 1,2,4,…,2^{k-1},n-2^k+1 1,2,4,…,2k−1,n−2k+1
int main() {
cin>>N>>V;
int cnt=0;
for(int i=1; i<=N; i++) {
int wi,vi,s;
cin>>wi>>vi>>s;
int k=1;
while(k<=s){
cnt++;w[cnt]=wi*k;v[cnt]=vi*k;
s-=k;k*=2;
}
if(s>0){
cnt++;w[cnt]=wi*s;v[cnt]=vi*s;
}
}
N=cnt;
for(int i=1; i<=N; i++) {
for(int j=V; j>=w[i]; j--)
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
}
cout<<dp[V]<<endl;
return 0;
}
- 混合背包
- 二维费用背包
for (int i = 1; i <= n; i++)
for (int j = V; j >= c[i]; j--)
for (int k = M; k >= g[i]; k--)
f[j][k] = max(f[j][k], f[j - c[i]][k - g[i]] + w[i]);
“二维费用”的条件多数是以这样一种隐含的方式给出的:最多只能取 M M M件物品。
- 分组背包
f [ k ] [ j ] = m a x ( f [ k − 1 ] [ j ] , f [ k − 1 ] [ j − c [ i ] ] + w [ i ] ∣ 物 品 i ⊆ 组 k ) f[k][j]=max(f[k−1][j],f[k−1][j−c[i]]+w[i]∣物品i⊆组k) f[k][j]=max(f[k−1][j],f[k−1][j−c[i]]+w[i]∣物品i⊆组k)
for (int i = 1; i <= n; i++) {
cin >> s; // 物品组数
for (int j = 1; j <= s; j++) cin >> c[j] >> w[j]; //组中每个物品的属性
for (int j = V; j >= 0; j--)//这一层循环一定要在枚举组内元素的循环外面
for (int k = 1; k <= s; k++)
if (j >= c[k])
f[j] = max(f[j], f[j - c[k]] + w[k]);
// 由于每组物品只能选一个,所以可以覆盖之前组内物品最优解的来取最大值
}
- 树上背包
- 求第K优解
int kth(int n, int V, int k) {
for (int i = 1; i <= n; i++) {
for (int j = V; j >= w[i]; j--) {
for (int l = 1; l <= k; l++) {
a[l] = f[j][l];
b[l] = f[j - w[i]][l] + v[i];
}
a[k + 1] = -1;
b[k + 1] = -1;
int x = 1, y = 1, o = 1;
while (o != k + 1 and (a[x] != -1 or b[y] != -1)) {
if (a[x] > b[y]) f[j][o] = a[x], x++;
else f[j][o] = b[y], y++;
if (f[j][o] != f[j][o - 1]) o++;
}
}
}
return f[V][k];
}