多重背包+二进制拆分 POJ1014
Posted -zzz-
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多重背包+二进制拆分 POJ1014相关的知识,希望对你有一定的参考价值。
题意:有权值分别为1,2,3,4,5,6的大理石,每种都有若干块,能否把它们分成权值相等的2份。大理石的总数量不超过20000。(多重背包)
分析:判断dp[ V/2 ] ==V/2 即可,但过程如果用普通做法会超时,即多重背包当成01背包做效率很低,这时候要用二进制拆分优化,将复杂度变为
二进制拆分原理:
这里是指一个大数11101111 ,只要每一位上的1我们都有一个数,就可以表示出来这个大数 也就是用1 2 4 8 (1 10 100 1000..) 可以表示出任意的数 ,那么任意一个数都可以经过处理变为2进制的数,为了方便每个二进制的数只有一个,这个处理可以看作把13 分为 1、2、4、6 (最后一个6是为了处理方便避免重复) 。
二进制拆分:
for(int i=1;i<=n;i++) for(int j=1;j<=num[i];j<<=1) //二进制每一位枚举. //注意要从小到大拆分 num[i]-=j;//减去拆分出来的 new_c[++tot]=j*c[i];//合成一个大的物品的体积 new_w[tot]=j*w[i];//合成一个大的物品的价值 if(num[i])//判断是否会有余下的部分. //就好像我们某一件物品为13,显然拆成二进制为1,2,4. //我们余出来的部分为6,所以需要再来一份. new_c[++tot]=num[i]*c[i]; new_w[tot]=num[i]*w[i]; num[i]=0;
Code:
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define INF 0x3f3f3f3f using namespace std; const int maxn=120012; int n,V; int dp[maxn]; int a[7]; int v[maxn],w[maxn]; int main() int kase=1; while(scanf("%d %d %d %d %d %d",&a[1],&a[2],&a[3],&a[4],&a[5],&a[6])!=EOF) if(a[1]==0&&a[2]==0&&a[3]==0&&a[4]==0&&a[5]==0&&a[6]==0) break; memset(dp,0,sizeof(dp)); printf("Collection #%d:\n",kase++); V=n=0; for(int i=1;i<=6;i++) V+=a[i]*i; if(V&1) printf("Can‘t be divided.\n\n"); continue; else int tot=0; for(int i=1;i<=6;i++) for(int j=1;j<=a[i];j<<=1) a[i]-=j; // printf("a[i]=%d j=%d\n",a[i],j); w[tot]=j*i; v[tot++]=j*i; if(a[i]!=0) v[tot]=a[i]*i; w[tot++]=a[i]*i; // printf("tot=%d\n",tot); for(int i=0;i<tot;i++) for(int j=V/2;j>=w[i];j--) dp[j]=max(dp[j],dp[j-w[i]]+v[i]); // for(int i=6;i>=1;i--) // for(int k=v/2;k>=i;k--) // for(int j=1;j<=a[i];j++) // if(k-i*j>=0) // dp[k]=max(dp[k],dp[k-i*j]+i*j); // //// printf("dp[k]=%d\n",dp[k]); // // // if(dp[V/2]==V/2) printf("Can be divided.\n\n"); else printf("Can‘t be divided.\n\n"); return 0;
以上是关于多重背包+二进制拆分 POJ1014的主要内容,如果未能解决你的问题,请参考以下文章