Dividing
Description
Input
Output
Sample Input
1 0 1 2 0 01 0 0 0 1 10 0 0 0 0 0
Sample Output
Collection #1:Can't be divided.Collection #2:Can be divided.
题意:有价值分别为1,2,3,4,5,6的弹珠,弹珠的数量分别为输入数据的n1,n2,n3,n4,n5,n6,问能否将其平均分成价值相等的两份
直接用多重背包写超时了,
f[i][v]=max{f[i-1][v],f[i-1][v-k*c[i]]+k*w[i]|0<=k<=n[i]}
复杂度是O(V*Σn[i])。
参考了别人的代码.
将其转化为0-1背包及完全背包问题
转化为01背包问题:把第i种物品换成n[i]件01背包中的物品,则得到了物品数为Σn[i]的01背包问题,直接求解,复杂度仍然是O(V*Σn[i])。
但是我们期望将它转化为01背包问题之后能够像完全背包一样降低复杂度。仍然考虑二进制的思想,我们考虑把第i种物品换成若干件物品,使得原问题中第i种物品可取的每种策略——取0..n[i]件——均能等价于取若干件代换以后的物品。另外,取超过n[i]件的策略必不能出现。
方法是:将第i种物品分成若干件物品,其中每件物品有一个系数,这件物品的费用和价值均是原来的费用和价值乘以这个系数。使这些系数分别为1,2,4,...,2^(k-1),n[i]-2^k+1,且k是满足n[i]-2^k+1>0的最大整数。例如,如果n[i]为13,就将这种物品分成系数分别为1,2,4,6的四件物品。
分成的这几件物品的系数和为n[i],表明不可能取多于n[i]件的第i种物品。另外这种方法也能保证对于0..n[i]间的每一个整数,均可以用若干个系数的和表示,这个证明可以分0..2^k-1和2^k..n[i]两段来分别讨论得出,
这样就将第i种物品分成了O(log n[i])种物品,将原问题转化为了复杂度为O(V*Σlog n[i])的01背包问题,是很大的改进。
下面给出O(log amount)时间处理一件多重背包中物品的过程,其中amount表示物品的数量:
procedure MultiplePack(cost,weight,amount)
if cost*amount>=V
{
CompletePack(cost,weight)
Return
}
integer k=1
while k<amount
{
ZeroOnePack(k*cost,k*weight)
amount=amount-k
k=k*2
}
ZeroOnePack(amount*cost,amount*weight)
#include<stdio.h>
#include<algorithm>
#include<string.h>
using namespace std;
int dp[200000];
int sum;
void cpl(int c,int w)//完全背包
{
int i;
for(i=c;i<=sum/2;i++)
{
if(dp[i]<dp[i-c]+w)
dp[i]=dp[i-c]+w;
}
}
void one(int c,int w)//0-1背包
{
int i;
for(i=sum/2;i>=c;i--)
{
if(dp[i]<dp[i-c]+w)
dp[i]=dp[i-c]+w;
}
}
void muty(int c,int w,int amount)//多重背包
{
int v;
if(c*amount>=sum/2) //相当于物体对于容量为sum/2的背包,个数无限多,所以当成完全背包处理
{
cpl(c,w);
return;
}
v=1;
while(v<=amount) //转化成0-1背包处理,用二进制压缩进行优化
{
one(c*v,w*v);
amount-=v;
v*=2;
}
one(c*amount,amount*w);
}
int main()
{
int a[10],i,j,k,num=0;
while(scanf("%d%d%d%d%d%d",&a[1],&a[2],&a[3],&a[4],&a[5],&a[6])!=EOF&&(a[1]!=0||a[2]!=0||a[3]!=0||a[4]!=0||a[5]!=0||a[6]!=0))
{
printf("Collection #%d:\n",++num);
memset(dp,0,sizeof(dp));
sum=(a[1]+a[2]*2+a[3]*3+a[4]*4+a[5]*5+a[6]*6);
if(sum%2!=0)
{
printf("Can't be divided.\n\n");
continue;
}
for(i=1;i<=6;i++)
{
if(a[i])
muty(i,i,a[i]);
}
if(dp[sum/2]==sum/2)
printf("Can be divided.\n");
else
printf("Can't be divided.\n");
printf("\n");
}}