zoukankan      html  css  js  c++  java
  • HDU 1059 Dividing(多重背包)

    HDU 1059 Dividing(多重背包)

    http://acm.hdu.edu.cn/showproblem.php?pid=1059

    题意:

           如今有价值为1,2,3,4,5,6的6种物品, 它们的数量为num[i]( 1<=i<=6 )个. 如今要问的是是否能把全部的的物品分成两份且这两份物品的价值总和同样 ?

    分析:

           首先我们求出全部物品的价值和sum_val, 假设sum_val是奇数, 那么明显不能分. 那么sum_val为偶时, 我们令m=sum_val/2. 我能仅仅要看看从全部物品里面取物品是否能使得: “全部取的物品总价值==m?”

           因为每一个物品的数目是num[i]个, 所以本题是一个多重背包问题.

    事实上多重背包问题能够转成01背包来解, 可是效率不高. 所以这里我们按<<背包九讲>>中提到的二进制压缩的思想来解决本题.

           令dp[i][j]==x 表示选前i种物品且总价值<=j的前提下, 全部被选物品能达到的最大价值.

           对于val[i]*num[i]>=m的物品来说, 我们直接用一次全然背包就可以.

           即dp[i][j] = max( dp[i-1][j] , dp[i][j-val[i]]+val[i] )

           对于val[i]*num[i]<m的第i类物品, 我们把它看成是以下的多个物品的组合体:

           1个 2个 4个… 2^(k-1)个 以及 num[i] – 2^k+1 个

           我们对由第i种物品划分成的上述每堆物品做一次01背包就可以.

           (为什么上面的划分能得到正确解?

    由于上面的物品覆盖了我们对num[i]个第i类物品的全部可能选择)

           终于所求: 看dp[n][m]是否等于m就可以.

    AC代码:

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int maxn=20000+5;
    
    int m;//DP最大不能超过的价值
    int dp[maxn*5];
    int val[]={1,2,3,4,5,6};//每种物品的价值
    int num[10];            //每种物品的数量
    
    //一次01背包过程
    void ZERO_ONE_PACK(int cost,int val)
    {
        for(int i=m;i>=cost;i--)
            dp[i] = max(dp[i], dp[i-cost]+val);
    }
    
    
    //一次全然背包过程
    void COMPLETE_PACK(int cost, int val)
    {
        for(int i=cost;i<=m;i++)
            dp[i] = max(dp[i], dp[i-cost]+val);
    }
    
    //一次多重背包过程
    void MULTIPLE_PACK(int cost,int val,int sum)
    {
        //全然背包
        if(cost*sum>=m)
        {
            COMPLETE_PACK(cost,val);
            return ;
        }
    
        //log(sum)次01背包
        int k=1;
        while(k<sum)
        {
            ZERO_ONE_PACK(cost*k,val*k);
            sum -=k;
            k=k*2;
        }
        ZERO_ONE_PACK(sum*cost,sum*val);
    }
    
    int main()
    {
        int kase=0;
        while(scanf("%d%d%d%d%d%d",&num[0],&num[1],&num[2],&num[3],&num[4],&num[5])==6)
        {
            if(num[0]+num[1]+num[2]+num[3]+num[4]+num[5]==0) break;
            printf("Collection #%d:
    ",++kase);
    
            int sum_val=0;
            for(int i=0;i<6;i++)
                sum_val+= val[i]*num[i];
            if(sum_val%2)
            {
                printf("Can't be divided.
    
    ");
                continue;
            }
    
            m=sum_val/2;//物品价值和的一半
            memset(dp,0,sizeof(dp));
            for(int i=0;i<6;i++)
            {
                MULTIPLE_PACK(val[i],val[i],num[i]);
            }
            printf("%s
    
    ",m==dp[m]?"Can be divided.":"Can't be divided.");
    
        }
        return 0;
    }
    

  • 相关阅读:
    django大全
    centos 下安装python3.6.2
    爬虫基础知识与简单爬虫实现
    HDU5950 Recursive sequence (矩阵快速幂加速递推) (2016ACM/ICPC亚洲赛区沈阳站 Problem C)
    ZOJ5833 Tournament(递归打表)
    ZOJ4067 Books(贪心)
    ZOJ4062 Plants vs. Zombies(二分+贪心)
    ZOJ4060 Flippy Sequence(思维题)
    洛谷P2568 GCD(线性筛法)
    2018.11.6刷题记录
  • 原文地址:https://www.cnblogs.com/gavanwanggw/p/7084543.html
Copyright © 2011-2022 走看看