目录
题目
有 N种物品和一个容量是 V的背包。
第 i 种物品最多有 si件,每件体积是 vi,价值是 wi
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。
接下来有 N 行,每行三个整数 vi,wi,si用空格隔开,分别表示第 i 种物品的体积、价值和数量。
输出格式
输出一个整数,表示最大价值。
数据范围
0<N≤1000
0<V≤2000
0<vi,wi,si≤2000输入样例
4 5 1 2 3 2 4 1 3 4 3 4 5 2
输出样例:
10
这一题多重背包问题和前面的01背包,完全背包问题有所不同,运用到了更好的思维方法如果数据比较弱的,直接暴力就可以过,但是如果数据较大肯定就是过不了的。
暴力方法如下(当然是数据较弱的时候)
我们可以讲si个物品都罗列出来,在用01背包的方法来写,就是在原来的基础上再加上一层循环,也不难理解
#include <bits/stdc++.h>
using namespace std;
int v[1050],w[1050],dp[1050],x[1050];
int main ()
{
int n,m;cin>>n>>m;
for (int i=0;i<n;i++)
{
cin>>v[i]>>w[i]>>x[i];
}
for (int i=0;i<n;i++)
{
for (int k=0;k<x[i];k++)
for (int j=m;j>=v[i];j--)
dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
}
cout<<dp[m];
}
这样是不是就将多重背包完美解决了呢?答案是否定的。这样枚举的方法用到的是朴素方法。怎么理解这个朴素方法呢?我们这样写是不是把有多少个一样的物品罗列多少个物品。当我们每一种物品都有2000个,有1000个,每个的体积又是2000。这样的数据是不是特别大,时间复杂度为一般的方法绝对过不了。
二进制优化
二进制优化有些类似于压缩的思想。一个物品有很多,我们有很多很多的选择方法,可以选1..2..3...n个。那么我们就需要一种很快的选择方法,而不是朴素的暴力筛选。我们二进制方法就是这样一种很好的方法。
例如,我们有10个硬币,我们怎么样快速的找出来7个?一般应该是一个一个选出来把。但是聪明的小杰(就是我)可以事先把硬币分成四堆,分别是1,2,4,3个硬币,我们只需要选择1+2+4就能选出来7个硬币。这种方法在数据比较大的时候优势尤为明显,就是很快。这种把一个数字分成一堆数字再相加的方法和我们所学的二进制方法非常相似,所以叫做二进制优化法。
因此如果要讨论10个一样的物品就转化成讨论4个不同的物品了。而n个一样的物品就转化成了log2n个不同的物品进行讨论
如果不明白的话,大家可以去看看这位老师讲的课:多重背包的二进制优化
下面是二进制优化的主要步骤
vi wi si
v[] w[]//定义的一些变量
int k=0;
int t=1;
while (si>=t)
{
k++;//k最后表示的是总共有多少的物品
v[k]=vi*t;
w[i]=wi*t;
wi-=t;t*=2;
}
if (si>0)
{
k++;
v[k]=si*vi;
w[i]=vi*wi;
}
下面看下我的代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5;
int v[N],w[N],dp[N];
signed main ()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n,m;cin>>n>>m;
int k=0;
for (int i=1;i<=n;i++)
{
int vi,wi,si;
cin>>vi>>wi>>si;
int t=1;
while (t<=si)
{
k++;
v[k]=vi*t;
w[k]=wi*t;
si-=t;
t*=2;
}
if (si>0)
{
k++;
v[k]=si*vi;w[k]=si*wi;
}
}//这里就是相当于预处理一下这些物品
//前面的一旦处理好,后面的内容就和01背包一样了
for (int i=1;i<=k;i++)//注意这里不是i<=n而是i<=k
{
for (int j=m;j>=v[i];j--)
dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
}
cout<<dp[m]<<'\n';
return 0;
}