完全背包问题

题目 

有N种物品和一个容量为V的背包,每种物品有无限件可以使用。放入第i种物品的费用是Ci,价值是Wi。求解:将哪些物品装入背包,可使这些物品的耗费的费用总和不超过背包的容量,且价值总和最大。

基本思路

从每种物品的角度考虑,与它相关的策略已并非取或不取两种,而是取0件、1件、2件......直至取(V/Ci)件等多种策略。

如果仍按照解01背包的思路,令F[i][v]表示前i种物品恰好放入一个容量为v的背包的最大权值。仍然可以按照每种物品不同的策略写出状态转移方程,像这样:

                                    F[i][v]=max(F[i-1][v-kCi]+kWi)  0<=kCi<=v

for(int i=1;i<=N;i++)
{
    for(int v=0;v<=V;v++)
    {
        for(int k=0;k*C[i]<=v;k++)
        {
            F[i][v]=max(F[i][v],F[i-1][v-k*C[i]]+k*W[i]);
        }
    }
}
printf("%d\n",F[N][V]);

将01背包问题的基本思路加以改进,得到这样一个清晰的方法,但该方法总的复杂度是比较大的,还需要加以优化。

一个简单有效的优化

1.若两件物品i,j满足Ci<=Cj且Wi>=Wj,则完全可以将物品j去掉,不用考虑。

2.若一件物品的费用Ci>V,那么该物品也可以直接去掉,不用考虑

转化为01背包求解

最简单的想法是,考虑到第i种物品最多可以选(V/Ci)件,于是可以把第i种物品转化为(V/Ci)件费用及价值均不变的商品,然后求解这个01背包问题。这样的做法完全没有改进时间复杂度,但这种方法也指明了将完全背包问题转化为01背包问题的思路:

                        将一种物品拆成多件只能选0件或1件的01背包中的物品

二进制优化

num=1;
for(int i=1;i<=N;i++)
{
    for(int j=1;C[i]*j<=V;j*=2)
    {
        value[num]=j*W[i];
        cost[num++]=j*C[i];
    }
}
for(int i=1;i<num;i++)
{
    for(int j=V;j>=cost[i];j--)
    {
        dp[j]=max(dp[j],dp[j-cost[i]]+value[i]);
    }
}
printf("%d\n",dp[V]);

复杂度为O(VN)的算法

//这个算法使用一维数组
for(int i=1;i<=N;i++)
{
    for(int v=C[i];v<=V;v++)
    {
        F[v]=max(F[v],F[v-C[i]]+Wi);
    }
}

    你会发现这个代码与01背包的代码只有v的循环次序不同而已。

    为什么这个算法就可行呢?首先想想为什么01背包中要按照v递减的次序来循环。让v递减是为了保证第i次循环中的状态F[i][v]是由状态F[i-1][v-Ci]递推而来。换句话说,这正是为了保证每件物品只选一次,保证在考虑"选入第i件物品"这个策略时,依据的时一个绝无已经选入第i件物品的子结果F[i-1][v-Ci]。而现在完全背包的特点恰是每种物品可选无限件,所以在考虑"加选一件第i种物品"这种策略时,却正需要一个可能已选入第i件物品的子结果F[i][v-Ci],所以就可以并且必须采用v的递增顺序循环。这就是为何这个简单程序成立的原理。

for(i=1;i<=N;i++)
{
   for(v=0;v<=V;v++)
   {
     if(v>=C[i])
        F[i][v]=max(F[i-1][v],F[i][v-C[i]]+W[i]);//注意此处与0-1背包的不同,0-1背包:max(V[i-1][v],F[i-1][v-C[i]+W[i])
     else
        F[i][v]=F[i-1][v];
   }
}
printf("%d\n",F[N][V]);

 

                                                                                                                                          摘自《背包九讲》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值