CH5E07 划分大理石(背包dp+二进制拆分)

    传送门

    大意:

  有价值分别为1..6的大理石各a[1..6]块,现要将它们分成两部分,使得两部分价值之和相等,问是否可以实现。其中大理石的总数不超过20000。

 解题思路:

  妥妥的多重背包+二进制拆分,主要写一下二进制拆分存个档(儿时的噩梦)。

  总所周知,20,21,22,……2k-1从中挑选若干个相加可以得到0~2k-1中的任意数。那么将一个数s进行二进制拆分,首先要做的就是找到最大k满足2k-1<=s,设c=s-2k+1。显而易见20,21,……,2k-1,c可以从中挑选若干个数相加得到0~s中的任意数。这题要做优化就是从这个思路中来,拿价值为1的大理石举例,设有t1个价值为1的大理石,可以将t1拆分为20,21,22,……,2k-1,c,然后就可以将多重背包化为01背包去解决,每个数s可以化解出log(s)个,总复杂度o(nlogn)

  

#include<bits/stdc++.h>
using namespace std;
int num[7][10086];
bool bk[100086];
int main()
{
    int t[10];
    while(1){
        int sum=0;
        for(int i=1;i<=6;i++){
            scanf("%d",&t[i]);
            sum+=t[i]*i;
        }
        if(sum==0)    break;
        if(sum&1){
            cout<<"Can't"<<endl;continue;
        }
        sum/=2;for(int i=1;i<=sum;i++)    bk[i]=false;bk[0]=true;
        for(int i=1;i<=6;i++){
            int t1=0,t2=0;
            while(t[i]>=(1<<t1)){
                num[i][t1]=(1<<t1);t1++;
            }
            if(t[i]-(1<<t1))    num[i][t1++]=t[i]-(1<<t1);
            for(int j=0;j<t1;j++){
                for(int h=sum;h>=i*num[i][j];h--){
                    bk[h]|=bk[h-i*num[i][j]];
                }
            }
        }
        if(bk[sum])    cout<<"Can"<<endl;
        else        cout<<"Can't"<<endl;
    }
}
View Code

 

posted @ 2020-04-08 23:37  Bear_2  阅读(207)  评论(0编辑  收藏  举报