完全背包

转自:(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  82-2i件物品选若干件放入空间为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.\n");
        else
            printf("The minimum amount of money in the piggy-bank is %d.\n",f[c]);
    }
    return 0;
}

因为三个背包加在一起太长,我都看不下去了,换个地方,
01背包:http://blog.csdn.net/zxy160/article/details/54378762
多重背包:http://blog.csdn.net/zxy160/article/details/54410915

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值