HDU1059——Dividing多重背包

开始最原始的背包方法,不断的TLE,代码如下

View Code
#include <stdio.h>
#include <string.h>
#define N 120010

int ans[N];
bool sign[N];

int main()
{
int a[7], i, j, l, sum, num1, num2, k = 1, x;
while(scanf("%d%d%d%d%d%d", &a[1], &a[2], &a[3], &a[4], &a[5], &a[6]) != EOF)
{
sum = 0;
for(i=1;i<=6;i++)
{
sum += (a[i]*i);
}
if(!sum)
{
break;
}
memset ( sign, 0, sizeof(sign) );
sign[0] = 1;
ans[0] = 0;
num1 = 1;
num2 = 1;
printf("Collection #%d:\n", k++);
if(sum&1)
{
printf("Can't be divided.\n");
continue;
}
for(i=1;i<=6;i++)
{
for(j=1;j<=a[i];j++)
{
for(l=0;l<num1;l++)
{
x = ans[l] + i;
if( !sign[x] )
{
sign[x] = 1;
ans[num2 ++] = x;
}
}
num1 = num2;
}
}
if(sign[sum/2])
{
printf("Can be divided.\n");
}
else
{
printf("Can't be divided.\n");
}
}
return 0;
}

看了一下Discuss,有些人用模2, 6, 30,60等数字过得,不过仔细想想可以发现这种方法是错误的,%2的可以试试2,1,0,0,0,0,%6的可以试试6,0,0,0,0,1,依次类推,用取模的方法得出的是“cna't”,但这些都是可以的。

上网搜结题报告,用的是01背包+二进制压缩,以下转至http://www.linuxso.com/linuxbiancheng/2457_2.html

更具体一点的知识可以参考:背包问题九讲

基本方法是转化为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-12^k..n[i]两段来分别讨论得出,并不难,希望你自己思考尝试一下。

这样就将第i种物品分成了O(log n[i])种物品,将原问题转化为了复杂度为O(V*Σlog n[i])01背包问题,是很大的改进。

 

 

代码:

View Code
#include <stdio.h>
#include <string.h>
#define N 1200010

bool f[N/2];

int main()
{
int a[7], i, j, l, sum, k = 1;
while(1)
{
for(i=1;i<=6;i++)
{
scanf("%d", a+i);
}
sum = 0;
for(i=1;i<=6;i++)
{
sum += (a[i]*i);
}
if(!sum)
{
break;
}
printf("Collection #%d:\n", k++);
if(sum&1)
{
printf("Can't be divided.\n\n");
continue;
}
sum /= 2;
memset ( f, 0, sizeof(f) );
f[0] = 1;

for(i=1;i<=6;i++)
{
for(j=1;j*2-1 <= a[i]; j *= 2)
{
for(l=sum;l>=j*i;l--)
{
f[l] |= f[l-j*i];
}
}
j = a[i]-(j-1);
if(j > 0)
{
for(l=sum;l>=j*i;l--)
{
f[l] |= f[l-j*i];
}
}
}
f[sum] ? printf("Can be divided.\n\n") : printf("Can't be divided.\n\n");
}
return 0;
}


posted @ 2011-11-26 10:44  1050768624  阅读(474)  评论(0编辑  收藏  举报