多重背包问题
多重背包问题
问题
有 \(N\) 种物品和一个容量是 \(V\) 的背包。
第 \(i\) 种物品最多有 \(s_i\) 件,每件体积是 \(v_i\),价值是 \(w_i\)。
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
题解
DP
相较于完全背包只需考虑背包容量,多重背包既要考虑背包容量,又要考虑背包容量足够时的物品数量:
#include<iostream>
#include<cmath>
using namespace std;
int n ,m ,v[105] ,w[105] ,s[105] ,f[105][105] ;
int main()
{
cin >> n >> m;
for(int i = 1 ;i <= n ;i ++)
{
cin >> v[i] >> w[i] >> s[i];
for(int j = 0 ;j <= m ;j ++)
for(int k = 0 ;k <= s[i] ;k ++)
if(k * v[i] <= j) f[i][j] = max(f[i][j] ,f[i - 1][j - k * v[i]] + k * w[i]);
}
cout << f[n][m] << endl;
return 0;
}
相较于完全背包问题中的无穷多个物品,多重背包问题中的有限物品数可将其转化为\(01\)背包问题。
#include<iostream>
#include<cmath>
using namespace std;
int n ,m ,a ,b ,s ,v[10005] ,w[10005] ,f[105] ,t ;
int main()
{
cin >> n >> m;
while(n --)
{
cin >> a >> b >> s;
while(s --)
{
v[++ t] = a;
w[t] = b;
}
}
for(int i = 1 ;i <= t ;i ++)
for(int j = m ;j >= v[i] ;j --)
f[j] = max(f[j] , f[j - v[i]] + w[i]);
cout << f[m] << endl;
return 0;
}
甚至不用另外的数组存储\(v_i\)以及\(w_i\):
#include<iostream>
#include<cmath>
using namespace std;
int dp[1005], n, m, v, w, s;
int main()
{
cin >> n >> m ;
while(n --)
{
cin >> v >> w >> s;
while(s --)
{
for(int j = m ;j >= v ;j --)
dp[j] = max(dp[j] ,dp[j - v] + w);
}
}
cout << dp[m] << endl;
return 0;
}
当然以上方式无法处理大量数据。
二进制优化
取第\(i\)种物品,共有\(s_i\)个,其中必定存在:
\[s_i=1+2+4+8+16+32+64+128+L+k
\]
其中\(k\)为二进制后余数。
可以注意到:
\[1+2=3\quad 001_{(2)}+010_{(2)}=011_{(2)}\\
1+4=5\quad 001_{(2)}+100_{(2)}=101_{(2)}\\
2+4=6\quad 010_{(2)}+100_{(2)}=110_{(2)}\\
1+2+4=7\quad 001_{(2)}+010_{(2)}+100_{(2)}=111_{(2)}\\
\]
显然可以将每组物品再次分组,再以\(01\)背包的方式解决,十进制数使用二进制数以运算:
#include<iostream>
#include<cmath>
using namespace std;
int n, m, a, b, s, v[100005], w[100005], f[100005];
int main()
{
cin >> n >> m;
int t = 1;
while(n --)
{
cin >> a >> b >> s;
int k = 1 ;
while(s >= k)
{
v[t] = k * a;
w[t ++] = k * b;
s -= k;
k *= 2;
}
if(s > 0)
{
v[t] = s * a;
w[t ++] = s * b;
}
}
for(int i = 1 ;i <= t ;i ++)
for(int j = m ;j >= v[i] ;j --)
f[j] = max(f[j] ,f[j - v[i]] + w[i]);
cout << f[m] << endl;
return 0;
}
也可将输入与规划相结合:
#include<iostream>
#include<cmath>
using namespace std;
int n, m, v, w, s, f[10000];
int main()
{
cin >> n >> m;
while(n --)
{
cin >> v >> w >> s;
for(int k = 1 ;k <= s ;k *= 2)
{
for(int j =m ;j >= v * k ;j --)
f[j] = max(f[j] , f[j - v * k] + w * k);
s -= k;
}
if(s)
for(int j = m ;j >= v * s ;j --)
f[j] = max(f[j] , f[j - v * s] + w * s);
}
cout << f[m] << endl;
return 0;
}

浙公网安备 33010602011771号