「这是我参与2022首次更文挑战的第4天,活动详情查看:2022首次更文挑战」。
多重背包问题
n个物品(每个物品有限个),每个物品v体积,w价值,有m个背包,如何取价值最大化
模板
优化前
#include <iostream>
using namespace std;
const int N = 110;
int f[N];
int n, m;
int main(){
cin >> n >> m;
for (int i = 1; i <= n; i ++ ){
int v, w, c;
cin >> v >> w >> c;
for (int j = m; j >= 0; j -- ) {
for (int k = 1; k * v <= j && k <= c; k ++ ){
f[j] = max(f[j], f[j - k * v] + w * k);
}
}
}
cout << f[m] << endl;
return 0;
}
优化后
本次优化是时间复杂度的优化(二进制拆分法)
思想:用0到2^k可以表示0到2^k-1的任何数,通过该思想将多重背包转化成01背包
#include <bits/stdc++.h>
using namespace std;
const int maxn=25000;
int N,V;
int w[maxn],v[maxn];
int dp[maxn];
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++) {//与01背包相同,因为将多重转化为二进制的01背包
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;
除了二进制拆分法,还有单调队列优化法,但是太难,不进行描述。
分组背包问题
n个物品(每个物品有限个),有很多组,每个组只能取一个,每个物品v体积,w价值,有m个背包,如何取价值最大化
可将每个组看成一个01背包问题,此时就将分组背包转化为多个01背包问题、
分析
F[ i , j ] = max ( F [ i - 1 , j ] , F [ i , j - v] + w )
该公式表示:F[ i , j ] 从前 i 个物品中选出了总体积为 j 的物品放入背包的最大价值和
F [ i - 1 , j ] 表示未选第 i 个物品的最大价值
max 1-k( F [ i , j - v ] + w )表示选第 i 组个物品k(选1,2,3……个的总和)1-k表示第k组
模板
求最大价值
#include <iostream>
using namespace std;
const int N = 110;
int v[N][N], w[N][N];
int f[N], s[N];
int n, m;
int main(){
cin >> n >> m;
for (int i = 1; i <= n; i ++ ){
cin >> s[i];
for (int j = 1; 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 = 1; 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;
return 0;
}
求方案数
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 11, M = 16;
int n, m;
int w[N][M];
int f[N][M];
int w0[N];
int main(){
cin >> n >> m;
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ )
cin >> w[i][j];
for (int i = 1; i <= n; i ++ )
for (int j = 0; j <= m; j ++ )
for (int k = 0; k <= j; k ++ )
f[i][j] = max(f[i][j], f[i - 1][j - k] + w[i][k]);
cout << f[n][m] << endl;
int j = m;
for (int i = n; i; i -- )
for (int k = 0; k <= j; k ++ )
if (f[i][j] == f[i - 1][j - k] + w[i][k])
{
w0[i] = k;
j -= k;
break;
}
for (int i = 1; i <= n; i ++ ) cout << i << ' ' << w0[i] << endl;
return 0;
}