您的位置:首页 > 其它

POJ 1014 -多重背包

2015-11-16 22:59 330 查看
http://poj.org/problem?id=1014

题意:

有6个价值为 1 2 3 4 5 6的物品,每个背包分别有Ai个

求是否能选够  总价值为n的 物品集合 

max_Ai=20000

所以max_n= 20000*6=12W;

我们把多重背包问题转化为01背包就方便很多了,但是显然不能看成 Ai个价值为j的物品 

这样的话 背包数最大是2W,j最大是12W,2W*12W超时了。

用到一个二进制优化

我们在dp的过程中,【需要把 选上1到Ai个物品的情况都要考虑到】,而我们 可以把 A【i】个价值一样的物品拆成log【Ai】+1堆物品,并把他们看成一个独立的物品 

如13个物品K 拆成 , 1 2 4 个(都是2^k),余下 6 (13-2^k,这个k是最大的且满足13-2^k>=0的数)

那么显然 由 这四个新的独立 物品堆,每个含1 2 4 6单位的K物品,  
【他们可以通过自由组合,得到j=1到13之间 任一个 j,   所有同时选择j个物品的情况都能被得到(1<=j<=13)】

至此,多重背包问题就转化为为了 一共有
∑A[i]个物品,最大体积为sum的0-1背包问题,复杂度为  O(   ∑A[i] *sum    )

本题里面约为  log(2W)* 12W 能过

#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
#include<map>
using namespace std;
int max(int a ,int b)
{
return a>b?a:b;
}
int tm[10];
int sum;
int ok;
int dp[120000];
struct node
{
int v;
int num;
};
node divide[15*6];
int main()
{
int i,j;

int cnt=1;
while(scanf("%d%d%d%d%d%d",&tm[1],&tm[2],&tm[3],&tm[4],&tm[5],&tm[6]))
{
sum=0;
ok=0;

for (i=1;i<=6;i++)
{
/* if (tm[i]>60) //60为1~6最小公倍数,任何一个tm[i]超过60,必然超出60*tm[i]的部分能被平均分配,所以只需要看60,61能否分配完成即可
{
if (tm[i]%2) tm[i]=61;
else tm[i]=60;
}
*/ sum+=tm[i]*i;
}
if (!sum) break;
if (cnt!=1) printf("\n");
printf("Collection #%d:\n",cnt++);
if (sum%2)
{ printf("Can't be divided.\n");continue;}

int cun=1;

for (i=1;i<=6;i++) //二进制拆分
{
int k=1;
int m=tm[i];
while(k<=m)
{
divide[cun].v=i;
divide[cun++].num=k;
m=m-k;
k=k*2;
}
if (!m) continue; //价值为0的物品集无意义
divide[cun].v=i;
divide[cun++].num=m;//余数
}
sum=sum/2;

memset(dp,0,sizeof(dp));
dp[0]=1;
for (i=1;i<cun;i++)
{
int tmp=divide[i].num*divide[i].v;//取得该物品的价值 ,物品的体积都看作1
if (!tmp) continue;
for (j=sum;j>=tmp;j--)
dp[j]=max(dp[j],dp[j-tmp]); //是否能选择到价值为j的物品,分为前i-1个物品恰好得到价值j的背包,或者前i-1个物品得到价值为j-tmp的背包;
}

if (dp[sum] ) ok=1;

if (ok)
printf("Can be divided.\n");
else
printf("Can't be divided.\n");

}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: