今天我们来学习完全背包,完全背包可以简化成01背包,先说下问题描述
题目
有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
完全背包是每种都有无限可用,但是我们会发现,一种物品最大的个数不可以超过V/c[i],因为容量有限,我们假设01背包的思路,得出状态转移方程
dp[i][v] = max(dp[i-1][v],dp[i-1][v-k*c[i]+k*w[i]]) 0<=k*c[i]<=V
我们发现这样的算法的复杂度非常高,我们需要简化算法,这时候呢,我们就引入把一种物品,拆成多件物品的思想,我们引入二进制拆解法,
更高效的转化方法是:把第i种物品拆成费用为c[i]*2^k、价值为w[i]*2^k的若干件物品,其中k满足c[i]*2^k<V。这是二进制的思想,因为不管最优策略选几件第i种物品,总可以表示成若干个2^k件物品的和。这样把每种物品拆成O(log(V/c[i]))件物品,是一个很大的改进。但我们有更优的O(VN)的算法。 * O(VN)的算法这个算法使用一维数组,先看伪代码: <pre class"example"> for i=1..N for v=0..V f[v]=max{f[v],f[v-c[i]]+w[i]};
具体二进制是什么思想呢
把它的件数C 用二进制分解成若干个件数的集合,这里面数字可以组合成任意小于等于C的件数,而且不会重复,之所以叫二进制分解,是因为这样分解可以用数字的二进制形式来解释
比如:7的二进制 7 = 111 它可以分解成 001 010 100 这三个数可以组合成任意小于等于7 的数,而且每种组合都会得到不同的数
15 = 1111 可分解成 0001 0010 0100 1000 四个数字
如果13 = 1101 则分解为 0001 0010 0100 0110 前三个数字可以组合成 7以内任意一个数,即1、2、4可以组合为1——7内所有的数,加上
0110 = 6 可以组合成任意一个大于6 小于等于13的数,比如12,可以让前面贡献6且后面也贡献6就行了。虽然有重复但总是能把 13 以内所有的数都考虑到了,基于这种思想去把多件物品转换为,多种一件物品,就可用01
背包求解了。
我们发现完全背包和01背包的区别就在于第二层循环,也就是体积循环的相反,因为加选一件第i种物品”这种策略时,却正需要一个可能已选入第i种物品的子结果f[i][v-c[i]],所以就可以并且必须采用v= 0..V的顺序循环。
再贴下完全背包的代码
#include<iostream>
#include<cstdio>
#include<string.h>
#include<string>
#include<set>
#include<algorithm>
#include<cmath>
#define ll __int64
#define MAX 100009
#define Max 10000009
using namespace std;
int c[MAX];
int v[MAX];
int w[MAX];
int dp[MAX];
int V;
int n;
int main()
{
int t;
cin>>t;
while(t--)
{
int x,y;
cin>>x>>y;
V = y - x;
for(int i = 1;i<=V;i++) dp[i] = Max;
cin>>n;
for(int i = 1;i<=n;i++)
{
cin>>v[i]>>w[i];
}
for(int i = 1;i<=n;i++)
{
for(int j = w[i];j<=V;j++)
{
dp[j] = min(dp[j],dp[j-w[i]]+v[i]);
}
}
if(dp[V]==Max)
cout<<"This is impossible."<<endl;
else
cout<<"The minimum amount of money in the piggy-bank is "<<dp[V]<<"."<<endl;
}
return 0;
}
443

被折叠的 条评论
为什么被折叠?



