=洛谷上找不到题了,没有传送门,不过凑合着看看吧
如果你还没有学过背包问题的话,推荐你看一下我的这一篇博客:
C++ 0/1背包(比数字三角形难1/inf左右的DP)_jinjiayang的博客-CSDN博客
题面
好不容易从洛谷上找到的题目,感谢
题目描述:
给出N个物品(一个物品可以选择0~a个),背包最大承重为M,每个物品有一个重量w,一个价值v,一个个数a。如何选择才能在重量不超过M的情况下,使选择的物品的价值总和最大。
输入格式:
第一行:是两个正整数N,M。
第2 ~ (n + 1)行:每行三个正整数w,v和a。
输出格式:
一个整数,为最大价值总和是多少。
输入样例:
4 20
9 3 3
9 5 1
4 9 2
1 8 3
1
2
3
4
5
输出样例:
47
题解
如果你看过0/1背包的话,下面所述的对你而言应该还是比较轻松的
判断
很明显,这一题同样满足最优子结构和无后效性原理,可使用动态规划解决
状态转移方程
在0/1背包中我们知道,状态转移方程是
dp[j] = max(dp[j-w[i]] + v[i], dp[j]);
那么在多重背包中,其实和0/1背包的状态转移方程是类似的,
但是!
我们发现,一个物品可以带的次数是有限制的,因此我们在创建一个变量k,代表我们选择了这个物品k次,状态转移方程就是
dp[j] = max(dp[j], dp[j-k*wgt[i]] + k*vls[i]);
其含义就不再多介绍了
代码
#include<bits/stdc++.h>
using namespace std;
int wgt[102], vls[102], sum[102], dp[102];
int n, c;
int main()
{
cin >> n >> c;
for(int i=1; i<=n; i++) cin >> wgt[i] >> vls[i] >> sum[i];
for(int i=1; i<=n; i++) for(int j=c; j>=wgt[i]; j--) for (int k=1; k<=sum[i] && k*wgt[i]<=j; k++)
{
dp[j] = max(dp[j], dp[j-k*wgt[i]] + k*vls[i]);
}
cout << dp[c];
}
二进制优化
此刻我们发现,我们的复杂度达到了惊人的O(n^3),也就是立方阶,因此我们考虑对其进行优化
转化
我们发现,买3件物品等于买1件+2件物品,买5件物品等于买1件+4件物品,
1=1, 2=2, 3=1+2, 4=4, 5=1+4, 6=2+4, 7=1+2+4, 8=8, 9=1+8, 10=2+8...
一个正整数肯定写成若干个二的幂相加的表示形式,即任何正整数皆可写成一个二进制数
因此我们可以把一个商品拆成1,2,4,8,16...n个商品,并将其转化为0/1背包进行求解!
最终优化代码
#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++) {
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;
}
不想敲键盘了,感谢@hnjzsyjyj的代码!
至此我们成功把n^3的复杂度降低为了n^2的!
二进制优化核心部分
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;
}
time to 点赞
看完后,别忘了
点赞!
收藏!
Thanks……