第一次写多重背包的题,竟然1A,我真的十分地感动,哈哈。
题意:Marsha 和 Bill 收集了一些弹珠,弹珠因为大小和花纹不同,有不同的价值,他们想把弹珠分成价值相同的两份,这样对两个人才公平,
问是否能够分成这样的两份。
#include <iostream>
using namespace std;
#define MAX(a, b) a>b?a:b
const int size = 7;
int dp[60010], amount[size];
int volume;
bool flag;
void ZeroOnePack(int c, int w)
{
for(int v = volume; v >= c; v--)
{
dp[v] = MAX(dp[v-c]+w, dp[v]);
if(dp[v] == volume)
{
flag = true;
break;
}
}
}
int main()
{
int cas = 0, i, v, total;
while(true)
{
cas++;
memset(dp, -1, sizeof(dp));
dp[0] = 0;
total = 0;
flag = false; //标记是否可分
for(i = 1; i < size; i++)
{
scanf("%d", &amount[i]);
total += i*amount[i];
}
if(total == 0) break;
if(total%2 == 0) //一个剪枝,当价值为偶数时才有可能可分,否则,flag 为false。
{
volume = total/2; //背包容量为弹珠总价值的一半,题意就是能否将这个背包装满。
for(i = 1; i < size && !flag; i++)
{
if(amount[i] == 0) continue;
if(i*amount[i] >= volume)
{
for(v = i; v <= volume; v++) //当价值为i的弹珠的总价值超过容量了,把问题转变成完全背包的问题。
{
dp[v] = MAX(dp[v-i]+i, dp[v]);
if(dp[v] == volume) //当背包已经满了,直接跳出循环,停止DP
{
flag = true;
break;
}
}
continue;
}
/*当不为完全背包时,采用二进制优化(具体参见dd_engi的背包九讲),将多件相同的物品合成一件进行01背包,优化时间*/
int k = 1;
while(k < amount[i] && !flag)
{
ZeroOnePack(k*i, k*i);
if(flag) continue;
amount[i] -= k;
k *= 2;
}
ZeroOnePack(amount[i]*i, amount[i]*i); /*此处包含amount[i]为1和amount[i] > 1且amount[i]*i < volume的情况
(即不执行上面的while循环和执行while循环的两种情况)*/
}
}
printf("Collection #%d:\n", cas);
if(flag)
printf("Can be divided.\n\n");
else
printf("Can't be divided.\n\n");
}
return 0;
}