zoukankan      html  css  js  c++  java
  • 多重背包问题 II

    原题链接AcWing5

    基本思考框架

    思路和多重背包问题I一样,但这题的数据范围变成1000了,非优化写法时间复杂度O(n^3) 接近 1e9必超时。
    优化多重背包的优化
    首先,我们不能用完全背包的优化思路来优化这个问题,因为每组的物品的个数都不一样,是不能像之前一样推导不优化递推关系的。(详情看下面引用的博文)
    我们列举一下更新次序的内部关系:

    c++ f[i , j ] = max( f[i-1,j] , f[i-1,j-v]+w , f[i-1,j-2*v]+2*w , f[i-1,j-3*v]+3*w , .....) 
    f[i , j-v]= max( f[i-1,j-v] , f[i-1,j-2*v] + w , f[i-1,j-2*v]+2*w , .....) 
    由上两式,可得出如下递推关系:
    f[i][j]=max(f[i,j-v]+w , f[i-1][j])
    

    接下来,我介绍一个二进制优化的方法
    假设有一组商品,一共有11个。我们知道,十进制数字 11可以这样表示

    11=1011(B)=0111(B)+(11−0111(B))=0111(B)+0100(B)

    正常背包的思路下,我们要求出含这组商品的最优解,我们要枚举12次(枚举装0,1,2....12个)。

    现在,如果我们把这11个商品分别打包成含商品个数为1个,2个,4个,4个(分别对应0001,0010,0100,0100)的四个”新的商品 “, 将问题转化为01背包问题,对于每个商品,我们都只枚举一次,那么我们只需要枚举四次 ,就可以找出这含组商品的最优解。 这样就大大减少了枚举次数。

    这种优化对于大数尤其明显,例如有1024个商品,在正常情况下要枚举1025次 , 二进制思想下转化成01背包只需要枚举10次。

    优化的合理性的证明

    先讲结论:上面的1,2,4,4是可以通过组合来表示出0~11中任何一个数的,还是拿11证明一下(举例一下):

    首先,11可以这样分成两个二进制数的组合:

    11=0111(B)+(11−0111(B))=0111(B)+0100(B)

    其中0111通过枚举这三个1的取或不取(也就是对0001(B),0010(B),0100(B)的组合),可以表示十进制数0~7( 刚好对应了 1,2,4 可以组合出 0~7 ) , 0~7的枚举再组合上0100(B)( 即 十进制的 4 ) ,可以表示十进制数 0~11。其它情况也可以这样证明。这也是为什么,这个完全背包问题可以等效转化为01背包问题,有木有觉得很奇妙

    怎么合理划分一个十进制数?
    上面我把11划分为

    11=0111(B)+(11−0111(B))=0111(B)+0100(B)

    是因为 0111(B)刚好是小于11的最大的尾部全为1的二进制 ( 按照上面的证明,这样的划分没毛病 ) , 然后那个尾部全为1的数又可以 分解为 0000....1 , 0000....10 , 0000....100 等等。

    对应c++代码:

    //设有s个商品,也就是将s划分
    for(int k = 1 ; k <= s ;k*=2)
    {
        s-=k;
        goods.push_back({v*k,w*k});
    }
    if(s>0) 
        goods.push_back({v*s,w*s});
    

    终究AC代码:01优化+二进制优化

    #include<iostream>
    #include<algorithm>
    #include<vector>
    using namespace std;
    const int N = 2010;
    int f[N],n,m;
    struct good
    {
        int w,v;
    };
    
    int main()
    {
        cin>>n>>m;
        vector<good> Good;
        good tmp;
    
        //二进制处理
        for(int i = 1 ; i <= n ; i++ )
        {
            int v,w,s;
            cin>>v>>w>>s;
            //坑,k <= s
            for(int k = 1 ; k <= s ; k*=2 )
            {
                s-=k;
                Good.push_back({k*w,k*v});
            }
            if(s>0) Good.push_back({s*w,s*v});
        }
    
        //01背包优化+二进制
        for(auto t : Good)
            for(int j = m ; j >= t.v ; j--)
                f[j] = max(f[j] , f[j-t.v]+t.w ); //这里就是f[j]
    
    
        cout<<f[m]<<endl;
        return 0;
    
    }
    
  • 相关阅读:
    液晶电子看板
    车间电子看板厂家
    车间电子看板系统
    Andon系统有哪些类型?
    Andon系统最完整的介绍
    Andon系统一般架构
    生产看板管理系统
    车间看板系统
    生产管理看板介绍
    黑马Java2020在线就业班2.1-全新升级
  • 原文地址:https://www.cnblogs.com/hnkjdx-ssf/p/14002508.html
Copyright © 2011-2022 走看看