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

原题: http://poj.org/problem?id=1014

一开始知道是多重背包,然后自己弄成01背包就交了,结果超时,所以百度后才知道需要用二进制优化

举个例子,假如说物品i有10个,单价为 2,而最后的最优结果取i是7个。传统的做法就是直接化成01背包,也就是把10个物品i看成10个一模一样的物品(w=1,v=2)然后送去dp

改良的方法就是压缩这个数量10: 我们可以把数量10二进制分解出来得到1 2 4  3(最后这个3是为了可以凑到刚好等于10)

则我们可以把10个物品看成四个物品 物① w=1,v=2; ② w=2 v=4 ③ w=4 v=8 ④ w=3 v=6 ,然后把这4个物品拿去dp,可以看出这样子数量就被我们压缩成4个了!

为什么可以把10二进制分解而结果正确?  因为我们最后选取的n=7个也是可以分成二进制,而这些个二进制必然是(1,2,4,3)中的某些个。

PS:小小的再优化:在多重背包中 如果某些物品的数量num[i]>sum / v[i],还可以把这个物品当成完全背包

启发来自: http://blog.csdn.net/qq_33171970/article/details/50582671

#include<stdio.h>
int main()
{
    const int size = 7;
    int num[size];
    int t = 0;
    while(true)
    {
        t++;
        int sum=0;
        for(int i=1;i<=6;i++)
        {
            scanf("%d",&num[i]);
            sum=sum+num[i]*i;    
        }        
        if(sum==0)break;
        if(sum%2==1)
        {
            printf("Collection #%d:\n",t);
            printf("Can't be divided.\n\n");
        }else{
            sum=sum/2;//看能不能凑到这个sum值 
            int dp[60010]={0};
            for(int i=1;i<=6;i++)//一共只有六种 
            {
                if(num[i]==0)continue;
                if(num[i]==1)//01背包 
                {
                    for(int j=sum;j>=i;j--)
                    {
                        if(dp[j]<dp[j-i]+i)
                        {
                            dp[j]=dp[j-i]+i;
                        }
                    }
                }else if(sum/i<=num[i])//完全背包 
                {
                    for(int j=i;j<=sum;j++)
                    {
                        if(dp[j]<dp[j-i]+i)
                        {
                            dp[j]=dp[j-i]+i;
                        }
                    }    
                }else{//多重背包 
                    //分割
                    int k = 1;//单个物品的重量 
                    int tmp = num[i];
                    while(k<=tmp)
                    {
                        int val = k*i;//单个物品的价值    
                        for(int j=sum;j>=val;j--)
                        {
                            if(dp[j]<dp[j-val]+val)
                            {
                                dp[j]=dp[j-val]+val;
                            }
                        }
                        k=k*2;
                        tmp=tmp-k;    
                    } 
                    if(tmp>0)
                    {
                        int val = tmp*i;//单个物品的价值    
                        for(int j=sum;j>=val;j--)
                        {
                            if(dp[j]<dp[j-val]+val)
                            {
                                dp[j]=dp[j-val]+val;
                            }
                        }
                    }
                }
            }
            printf("Collection #%d:\n",t);
            if(dp[sum]==sum)
            {
                printf("Can be divided.\n\n");
            }else{
                printf("Can't be divided.\n\n");
            }
        } 
    }
    return 0;
} 


猜你喜欢

转载自blog.csdn.net/zark721/article/details/77600659