多重揹包的複雜度爲O(n * m * k),其中n爲揹包總容量,m爲物品種類數,k爲一種物品的數量。實際上等價於01揹包。
二進制優化,是將一種物品的數量分解成1, 2, 4, 8, …, 2x, k-2x+1+1份並組成新的物品,注意除了最後一個數,其他數都是2的冪次。此時選擇任意數量的該原物品,都可以等價表示成選擇分解後的某幾個新物品。選擇新物品的任意組合,都可以表示成選擇一定數量的原物品。於是轉化爲物品數目m * 的01揹包。因此複雜度爲O(n * m * )。
題意
6種物品,權重爲1到6,給出數量。求是否可能均分爲兩份權重相同的?
分析
顯然這是一個多重揹包,總容量是總權重值,物品容量是其權重。求dp[n/2]是否有解。普通多重揹包會TLE,所以要用二進制優化,將6種物品分解成新的物品,轉化爲01揹包
代碼
// 二進制優化多重揹包
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAX_N = 20000;
bool dp[6*MAX_N+1];
int vals[6*MAX_N+1];
int main()
{
int n[7];
int k = 0;
while (++k)
{
int sum = 0;
int cnt = 0;
for (int i = 1; i <= 6; i++)
{
scanf("%d", &n[i]);
sum += i * n[i];
// 將num個同類型物品捆綁爲一個新物品
// 可以用新物品的組合表示任意數量的原物品
int num = 1;
while (num <= n[i])
{
vals[cnt++] = num*i;
n[i] -= num;
num *= 2;
}
if (n[i] > 0) vals[cnt++] = i*n[i];
}
// 轉化爲01揹包
if (sum == 0) break;
memset(dp, 0, sizeof(dp));
dp[0] = true;
for (int i = 0; i < cnt; i++)
for (int j = sum; j >= vals[i]; j--)
dp[j] |= dp[j-vals[i]];
printf("Collection #%d:\n", k);
if (sum % 2 == 0 && dp[sum/2] == true)
printf("Can be divided.\n\n");
else
printf("Can't be divided.\n\n");
}
}