转自:(http://blog.csdn.net/wumuzi520/article/details/7014830)
完全背包是在N种物品中选取若干件(同一种物品可多次选取)放在空间为V的背包里,每种物品的体积为C1,C2,…,Cn,与之相对应的价值为W1,W2,…,Wn.求解怎么装物品可使背包里物品总价值最大。
动态规划(DP):
1) 子问题定义:F[i][j]表示前i种物品中选取若干件物品放入剩余空间为j的背包中所能得到的最大价值。
2) 根据第i种物品放多少件进行决策
(2-1)
其中F[i-1][j-K*C[i]]+K*W[i]表示前i-1种物品中选取若干件物品放入剩余空间为j-K*C[i]的背包中所能得到的最大价值加上k件第i种物品;
设物品种数为N,背包容量为V,第i种物品体积为C[i],第i种物品价值为W[i]。
f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k*c[i]<=v}
举例:表2-1为一个背包问题数据表,设背包容量为10根据上述解决方法可得到对应的F[i][j]如表2-2所示,最大价值即为F[6][10].
表2-1背包问题数据表
物号i 1 2 3 4 5 6
体积C 3 2 5 1 6 4
价值W 6 5 10 2 16 8
表2-2前i件物品选若干件放入空间为j的背包中得到的最大价值表
0 1 2 3 4 5 6 7 8 9 10
0 0 0 0 0 0 0 0 0 0 0 0
1 0 0 0 6 6 6 12 12 12 18 18
2 0 0 5 6 10 11 15 16 20 21 25
3 0 0 5 6 10 11 15 16 20 21 25
4 0 2 5 7 10 12 15 17 20 22 25
5 0 2 5 7 10 12 16 18 21 23 26
6 0 2 5 7 10 12 16 18 21 23 26
解释1 具体背包中放入那些物品的求法和01背包情况差不多,从F[N][V]逆着走向F[0][0],设i=N,j=V,如果F[i][j]==F[i][j-C[i]]+W[i]说明包里面有第i件物品,同时j -= C[i]。完全背包问题在处理i自减和01背包不同,01背包是不管F[i][j]与F[i-1][j-C[i]]+W[i]相不相等i都要减1,因为01背包的第i件物品要么放要么不放,不管放还是不放其已经遍历过了,需要继续往下遍历而完全背包只有当F[i][j]与F[i-1][j]相等时i才自减1。因为F[i][j]=F[i-1][j]说明背包里面不会含有i,也就是说对于前i种物品容量为j的背包全部都放入前i-1种物品才能实现价值最大化,或者直白的理解为前i种物品中第i种物品物不美价不廉,直接被筛选掉。
同样可以转换成一维数组来表示:
for i=1..N
for v=0..V
f[v]=max{f[v],f[v-c[i]]+w[i]}
顺序
想必大家看出了和01背包的区别,这里的内循环是顺序的,而01背包是逆序的。
现在关键的是考虑:为何完全背包可以这么写?
在次我们先来回忆下,01背包逆序的原因?是为了是max中的两项是前一状态值,这就对了。
那么这里,我们顺序写,这里的max中的两项当然就是当前状态的值了,为何?
因为每种背包都是无限的。当我们把i从1到N循环时,f[v]表示容量为v在前i种背包时所得的价值,这里我们要添加的不是前一个背包,而是当前背包。所以我们要考虑的当然是当前状态。
这里同样给大家一道题目:(http://acm.hdu.edu.cn/showproblem.php?pid=1114)
思路:多重背包和以上两种的不同点在于,多重背包给了具体的重量,价钱,数量,可以转换为01背包求解(代码1),也可以进行二进制优化,将第i种物品分成若干件物品,其中每件物品有一个系数,这件物品的费用和价值均是原来的费用和价值乘以这个系数。使这些系数分别为 1,2,4,…,2^(k-1),n[i]-2^k+1,且k是满足n[i]-2^k+1>0的最大整数。例如,如果n[i]为13,就将这种 物品分成系数分别为1,2,4,6的四件物品,这种方法有模板(代码2),这样就将第i种物品分成了O(log n[i])种物品,将原问题转化为了复杂度为O(V*Σlog n[i])的01背包问题,是很大的改进,还有楼天城的单调队列法,表示不会。。。
代码
#include<stdio.h>
#include<algorithm>
using namespace std;
#include<string.h>
#define mem(a,b) memset(a,b,sizeof(a));
#define INF 0x3fffffff
int v[50005],w[10005];
int f[10005];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
mem(v,0);
mem(w,0);
mem(f,0);
int E,F;
scanf("%d%d",&E,&F);
int c=F-E;
for(int i=1; i<=c; i++)
{
f[i]=INF;
}
int u;
scanf("%d",&u);
for(int i=1; i<=u; i++)
{
int n,m;
scanf("%d%d",&n,&m);
v[i]=n;
w[i]=m;
}
for(int i=1; i<=u; i++)
{
for(int j=w[i]; j<=c; j++)
{
f[j]=min(f[j],f[j-w[i]]+v[i]);
}
}
if(f[c]==INF)
printf("This is impossible.
");
else
printf("The minimum amount of money in the piggy-bank is %d.
",f[c]);
}
return 0;
}
因为三个背包加在一起太长,我都看不下去了,换个地方,
01背包:http://blog.csdn.net/zxy160/article/details/54378762
多重背包:http://blog.csdn.net/zxy160/article/details/54410915