hdu1059 Dividing(多重背包二进制优化)

题目链接:hdu1059

题目大意:分别给出6种不同质量石头的数量  质量分别分1,2,3,4,5,6;问能否均分

题目思路:如果用普通的多重背包  他会超时

这时需要我们队多重背包进行二进制的优化

转化为 01 背包求解:把第 i 种物品换成 n[ i ] 01 背包中的物品,则得到了物品数为 Σn [ i ] 01 背包问题
当然这样直接求解的复杂度仍然是 O(V* Σn [ i ])
我们考虑把第 i 种物品换成若干件物品,使得原问题中第 i 种物品可取的每种策略 —— 0..n[ i ] —— 均能等价于取若干件代换以后的物品。另外,取超过 n[ i ] 件的策略必不能出现。
方法是:将第 i 种物品分成若干件物品,其中每件物品有一个系数,这件物品的费用和价值均是原来的费用和价值乘以这个系数。使这些系数分别为 1,2,4,...,2^(k-1),n[ i ]-2^k+1 ,且 k 是满足 n[ i ]-2^k+1>0 的最大整数 。例如,如果 n[ i ] 13 ,就将这种物品分成系数分别为 1,2,4,6 的四件物品。

n[ i ] 拆成 1,2,4,...,2^(k-1),n[ i ]-2^k+1 ,( k 是满足 n[ i ]-2^k+1>0 的最大整数)道理何在?
1 1+2+4+...+2^(k-1)+n[ i ]-2^k+1= n[ i ]   这就保证了最多为 n[ i ] 个物品
2 1,2,4,……,2^(k-1), 可以凑出 1 2^k –1 的所有整数(联系一个数的二进制拆分即可证明)
3 2^k …… n[ i ] 的所有整数可以用若干个上述元素凑出(可以理解为凑 n[ i ]-t, n[ i ] 为上面所有数的和, t 则是一个小于 2^k 的数,那么在所有的数中去掉组成 2^k 的那些数剩下的就可以组成 n[ i ]-t 了)


复杂度为O(V*Σlog n[i])

很圆满的解决了问题
#include<stdio.h>
#include<string.h>
#define max(a,b) a>b?a:b
int Va[100],n[7],dp[200000];

int main(){
    int c=0;
    while(~scanf("%d%d%d%d%d%d",&n[1],&n[2],&n[3],&n[4],&n[5],&n[6]))
	{
		if(n[1]==0&&n[2]==0&&n[3]==0&&n[4]==0&&n[5]==0&&n[6]==0)break;
        printf("Collection #%d:\n",++c);
        int i,j,sum=0,mid;
        for(i=1;i<=6;i++)sum+=i*n[i];
        if(sum%2){//如果奇数的话  不用分 直接输出不能够均分
            printf("Can't be divided.\n\n");
            continue;
        }
        else mid=sum/2;
        memset(dp,0,sizeof(dp));
        int count=0,temp;
        for(i=1;i<=6;i++){
            temp=1;
            while(n[i]>=temp){
                Va[++count]=i*temp;//每个新的分配而成的物品的重量
                n[i]-=temp;
                temp*=2;
            }
            if(n[i]>0){
                Va[++count]=i*n[i];
            }
        }
        dp[0]=1;
        for(i=1;i<=count;i++){
            for(j=mid;j>=Va[i];j--){
                if(dp[j-Va[i]]){
                    dp[j]=1;
                }
            }
        }
        if(dp[mid]>0)printf("Can be divided.\n\n");
        else printf("Can't be divided.\n\n");
    }
    return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值