多重背包二进制优化(dp)

本题作为多重背包二进制优化模板

思路:一个数是能用二进制表达出来的,多重背包有数量限制且按照常规三个for容易超时,因为第三个循环对数量的处理是逐个进行的,比如有10个,那就要执行10次.

因此通常我们把数量拆分,比如说10可以分装成(1+2+4)+3,那4是怎么来的,这个4是二进制的结束点,10-(1+2)>2²,所以继续进行,10-(1+2+4)<2³ ,即停止,此时余数为3,。二进制有个结论就是(2⁰+2¹+2²+…2ⁿ )的和∑最接近且小于X时,0~∑之间所有数都能被这个2的次幂组合出来.

比如10里面的1可以由1得到,2由2得到,3由1+2得到,4由4得到,5由1+4得到,只需要加上X-∑(余数),那么0~10全部数字都可以表示出来,这样我们相当于把几个物品合成一个,对应的价值和质量也就翻了几倍,就像炼铁一样。

这一题可以看成给你一个数y,
你要用小于y的数x1,x2,x3,…xn加起来等于y,y可以看成背包问题中的容量同时也是价值的上限,xn可看成体积与价值都等于xn的物品,然后判断最大值是否为价值上限y,是即填满了背包。

这里时间复杂度变成了O(n*logm);
比如10≈2³,即可拆分成1,2,4三个产品,执行了三次循环

#include<stdio.h>
#include<string.h>
#define max(a,b) a>b?a:b
int value[200005],dp[200005];
int main()
{
    int a[7],r=0;

    scanf("%d %d %d %d %d %d",&a[1],&a[2],&a[3],&a[4],&a[5],&a[6]);
    while((a[1]+a[2]+a[3]+a[4]+a[5]+a[6])!=0)
    {

        int n=0,m=0,j,t=0,k,i,u,y,x,e,b,c,d;
        r++;x=(a[1]+a[2]*2+a[3]*3+a[4]*4+a[5]*5+a[6]*6);
        if(x%2!=0)
        {
            printf("Collection #%d:\nCan't be divided.\n\n",r);
        }
        else
        {
            x=x/2;
             for(i=1;i<=6;i++)
        {
            c=1;e=a[i];
            while(c<e)
            {
                t++;
                value[t]=c*i;

                e=e-c;
                c=c*2;

            }
            t++;

            value[t]=e*i;
        }


value[0]=0;
        for(i=0;i<=t;i++)
        {
            for(j=x;j>=value[i];j--)
            {
                if(i==0&&j==0)
                {
                    dp[j]=0;
                }
                else if(i==0&&j!=0)
                {
                    dp[j]=-999999;      
                }

                else
                {
                    dp[j]=max(dp[j],dp[j-value[i]]+value[i]);
                }
            }
        }
if(dp[x]==x)
{
    printf("Collection #%d:\nCan be divided.\n",r);
}
else
{
    printf("Collection #%d:\nCan't be divided.\n",r);
}
printf("\n");
        }

      scanf("%d %d %d %d %d %d",&a[1],&a[2],&a[3],&a[4],&a[5],&a[6]);

    }




return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值