POJ 1014 -多重背包

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;
} 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值