• 多重背包+二进制拆分 POJ1014


    题意:有权值分别为1,2,3,4,5,6的大理石,每种都有若干块,能否把它们分成权值相等的2份。大理石的总数量不超过20000。(多重背包)

    分析:判断dp[ V/2 ] ==V/2 即可,但过程如果用普通做法会超时,即多重背包当成01背包做效率很低,这时候要用二进制拆分优化,将复杂度变为 image.png

    二进制拆分原理:image.png

    image.png

    这里是指一个大数11101111 ,只要每一位上的1我们都有一个数,就可以表示出来这个大数   也就是用1 2 4 8 (1 10 100 1000..)  可以表示出任意的数 ,那么任意一个数都可以经过处理变为2进制的数,为了方便每个二进制的数只有一个,这个处理可以看作把13 分为 1、2、4、6 (最后一个6是为了处理方便避免重复) 。

    二进制拆分:

    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=num[i];j<<=1)
        //二进制每一位枚举.
        //注意要从小到大拆分
        {
            num[i]-=j;//减去拆分出来的
            new_c[++tot]=j*c[i];//合成一个大的物品的体积
            new_w[tot]=j*w[i];//合成一个大的物品的价值
        }
        if(num[i])//判断是否会有余下的部分.
        //就好像我们某一件物品为13,显然拆成二进制为1,2,4.
        //我们余出来的部分为6,所以需要再来一份.
        {
            new_c[++tot]=num[i]*c[i];
            new_w[tot]=num[i]*w[i];
            num[i]=0;
        }
    }
    View Code

    Code:

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define INF 0x3f3f3f3f
    using namespace std;
    
    const int maxn=120012;
    int n,V;
    int dp[maxn];
    int a[7];
    int v[maxn],w[maxn];
    int main(){
        int kase=1;
        while(scanf("%d %d %d %d %d %d",&a[1],&a[2],&a[3],&a[4],&a[5],&a[6])!=EOF){
            if(a[1]==0&&a[2]==0&&a[3]==0&&a[4]==0&&a[5]==0&&a[6]==0) break;
            memset(dp,0,sizeof(dp));
            printf("Collection #%d:
    ",kase++);
            V=n=0;
            for(int i=1;i<=6;i++){
                V+=a[i]*i;
            }
            if(V&1){
                printf("Can't be divided.
    
    ");
                continue;
            }
            else{            
                int tot=0;
                for(int i=1;i<=6;i++){
                    for(int j=1;j<=a[i];j<<=1){
                        a[i]-=j;
    //                    printf("a[i]=%d j=%d
    ",a[i],j);
                        w[tot]=j*i;
                        v[tot++]=j*i;
                    }
                    if(a[i]!=0){
                        v[tot]=a[i]*i;
                        w[tot++]=a[i]*i;
                    }
                }
    //            printf("tot=%d
    ",tot);
                for(int i=0;i<tot;i++){
                    for(int j=V/2;j>=w[i];j--){
                        dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
                    }
                }
    //            for(int i=6;i>=1;i--){
    //                for(int k=v/2;k>=i;k--){
    //                    for(int j=1;j<=a[i];j++){
    //                        if(k-i*j>=0)
    //                            dp[k]=max(dp[k],dp[k-i*j]+i*j);
    //                        
    ////                        printf("dp[k]=%d
    ",dp[k]);
    //                    }
    //                }
    //            }    
                if(dp[V/2]==V/2)
                    printf("Can be divided.
    
    ");
                else
                    printf("Can't be divided.
    
    ");
            }
        }
        return 0;
    }
    View Code
  • 相关阅读:
    android 通过GPS获取用户地理位置并监听位置变化
    Android Activity中启动另一应用程序的方法,无需得到类名
    每秒处理10万订单的支付架构 乐视集团
    Android进阶篇百度地图获取地理信息
    Windows 7 暗藏的管理功能上帝模式(GodMode)
    MSDN Webcast 微软SDLC最佳实践系列课程(3) 报表服务
    ALM Networks 开篇
    Delphi下如何使程序在Win7/Vista上用管理员权限运行
    2008年微软(北京).NET俱乐部年会 照片纪实
    如何打造大学生“亮剑团队”?
  • 原文地址:https://www.cnblogs.com/-Zzz-/p/11415834.html
Copyright © 2020-2023  润新知