Problem
acm.hdu.edu.cn/showproblem.php?pid=1059
题意
6 种宝石,价值分别是 1 到 6。分别给出 6 种宝石的数量,问能不能分成等价值的两堆。
分析
多重背包。主要是记录下多重背包的写法。
对每一种宝石,如果这种宝石的总价值超过所有宝石总价值的一半(因为要对半分),就对它跑一遍完全背包;否则,将这种宝石的数量拆开成:
num = 1 + 2 + 2^2 + 2^3 + …+2^n + rest
这种形式,用 数量*单价 组成若干个大宝石,然后对每个大宝石跑一遍 0/1 背包。(二进制优化,跟快速幂差不多)
Source code
#include <stdio.h>
#include <string.h>
#define N 20000
int num[7], dp[N*3+1];
int max(int a, int b)
{
return a > b ? a : b;
}
void zero_one(int sum, int v)
{
int j;
for(j=sum; j>=v; --j)
dp[j] = max(dp[j], dp[j-v] + v);
}
void complete(int sum, int v)
{
int j;
for(j=v; j<=sum; ++j)
dp[j] = max(dp[j], dp[j-v] + v);
}
void multiple(int sum, int i)
{
int k;
if(i * num[i] > sum)
complete(sum, i);
else
{
// 拆成:连续的二进制幂的和 + 剩下的
for(k=1; k<=num[i]; num[i]-=k, k<<=1)
zero_one(sum, k * i);
if(num[i])
zero_one(sum, num[i] * i);
}
}
int main()
{
int kase;
for(kase=1; ; ++kase)
{
int i, sum;
for(sum=0,i=1; i<7; ++i)
{
scanf("%d", num + i);
sum += num[i] * i;
}
if(!sum) break;
printf("Collection #%d:\n", kase);
if(sum & 1)
{
puts("Can't be divided.\n");
continue;
}
sum >>= 1;
memset(dp, 0, sizeof dp);
for(i=1; i<7; ++i)
multiple(sum, i);
if(dp[sum] == sum)
puts("Can be divided.\n");
else
puts("Can't be divided.\n");
}
return 0;
}