完全背包

问题描述:

一个旅行者随身携带一个背包,可以放入背包的物品有n种,每种物品的重量和价值分别是wi,viwi,vi,如果背包的最大容量限制是bb,每种物品可以放多个,怎样选择放入背包的物品以使得背包的价值最大?

        1) 子问题定义:F[i][j]表示前i种物品中选取若干件物品放入剩余空间为j的背包中所能得到的最大价值。

        2) 根据第i种物品放多少件进行决策

        其中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]。

       与01背包相同,完全背包也需要求出NV个状态F[i][j]。但是完全背包求F[i][j]时需要对k分别取0,…,j/C[i]求最大F[i][j]值,耗时为j/C[i]。那么总的时间复杂度为O(NV∑(j/C[i]))
 

伪代码如下:

 F[0][] ← {0}
 
     F[][0] ← {0}
 
     for i←1 to N
 
         do for j←1 to V
 
             do for k←0 to j/C[i]
 
                if(j >= k*C[i])
 
                     then F[i][k] ← max(F[i][k],F[i-1][j-k*C[i]]+k*W[i])
 
     return F[N][V]

 简单优化:

        若两件物品满足C[i] ≤C[j]&&W[i] ≥W[j]时将第j种物品直接筛选掉。因为第i种物品比第j种物品物美价廉,用i替换j得到至少不会更差的方案。

       这个筛选过程如下:先找出体积大于背包的物品直接筛掉一部分(也可能一种都筛不掉)复杂度O(N)。利用计数排序思想对剩下的物品体积进行排序,同时筛选出同体积且价值最大的物品留下,其余的都筛掉(这也可能一件都筛不掉)复杂度O(V)。

完全背包问题有一个很简单有效的优化,是这样的:若两件物品i、j 满足Ci ≤ Cj且Wi ≥ Wj,则将可以将物品j 直接去掉,不用考虑。这个优化的正确性是显然的:任何情况下都可将价值小费用高的j 换成物美价廉的i,得到的方案至少不会更差。对于随机生成的数据,这个方法往往会大大减少物品的件数,从而加快速度。然而这个并不能改善最坏情况的复杂度,因为有可能特别设计的数据可以一件物品也去不掉。

优化代码:

int main()
{

    int n,w;
    cin>>n>>w;
    map<int,int > m;

    int i,wi,vi;

    for(i=0;i<n;i++)
    {
        cin>>wi>>vi;
        if(wi<=w)  // 费用大于w的去掉
        {
            if(m.find(wi)!=m.end())
            {
                if(m[wi]<vi)  //费用(体积)相同时 选择更大的价值
                {
                    m[wi]=vi;
                }
            }
            else
            {
                m[wi]=vi;
            }
        }
    }

    map<int,int>::iterator it;
    it=m.begin();

    while(it!=m.end())
    {
        cout<<it->first<<"    "<<it->second<<endl;
        it++;
    }

    return 0;
}

转化为01背包

01 背包问题是最基本的背包问题,我们可以考虑把完全背包问题转化为01 背包问题来解。 
最简单的想法是,考虑到第i 种物品最多选⌊V /Ci⌋ 件,于是可以把第i 种物品转化为⌊V /Ci⌋ 件费用及价值均不变的物品,然后求解这个01 背包问题。这样的做法完全没有改进时间复杂度,但这种方法也指明了将完全背包问题转化为01 背包问题的思路:将一种物品拆成多件只能选0 件或1 件的01 背包中的物品。更高效的转化方法是:把第i 种物品拆成费用Ci * 2^k、价值为Wi * 2^k 的若干件物品,其中k 取遍满足Ci*2^k ≤ V 的非负整数。这是二进制的思想。因为,不管最优策略选几件第i 种物品,其件数写成二进制后,总可以表示成若干个2^k 件物品的和。这样一来就把每种物品拆成O(log ⌊V /Ci⌋) 件物品,是一个很大的改进。

代码如下:

int main()
{
    int n;
    cin>>n;
    while(n--)
    {

        int money;
        cin>>money;

        int i;

        for(i=0;i<3;i++)
        {
            for(int j=0;j<=money;j++)
            {
                if(j>=v[i])
                {
                    dp_1[j]=max(dp_1[j],dp_1[j-v[i]]+v[i]);
                }
            }
        }

        cout<<money-dp_1[money]<<endl;
    }
    return 0;
}

抽象函数伪代码

完全背包的思想和01背包的思想非常相似,两者的区别就在01背包是一个物体只有一个,而完全背包的物体是多个的。如果能把这一点给搞懂就很好理解了。

01背包具体参考这篇博客

https://mp.csdn.net/mdeditor/88808637#

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值