这道题我用的是最原始的DFS,虽然加了cache,避免了一些重复运算,但不出意料地TLE了。然后就是“优化”。把每个value中的item个数减到200以下(看discuss可以取到30甚至6的,还有一堆mod的解法)。我都不明白这些优化的原理。见源代码中注释掉的DFS部分。
1014 | Accepted | 3736K | 422MS | G++ | 2013B |
看到discuss中提到这道题可以用背包做。然后看了传说中的背包九讲,大致了解了,最终AC了。链接见https://github.com/tianyicui/pack。
1014 | Accepted | 916K | 16MS | G++ | 3727B |
然后就可以写出朴素的多重背包解法:
int goal = sum / 2;
fill(dp.begin(), dp.begin() + goal + 1, INT_MIN);
dp[0] = 0;
for (int i = 0; i < 6; ++i)
{
for (int w = 1; w <= nums[i]; ++w)
{
for (int v = goal; v >= (i + 1) * w; --v)
{
dp[v] = max(dp[v], dp[v - (i + 1) * w] + (i + 1) * w);
}
}
}
if (dp[goal] == goal)
{
cout << "Collection #" << seq << ":" << endl;
cout << "Can be divided." << endl;
cout << endl;
}
else
{
cout << "Collection #" << seq << ":" << endl;
cout << "Can't be divided." << endl;
cout << endl;
}
这道题实质是看价值goal能不能通过在6个物品中各拿一定件数获得,所以最终判断dp[goal]==goal.中间的状态转移也是对每件物品的每个件数,看拿之前的那个价值能不能到达这个价值。初始化可以都初始成0,这样表示所有价值中只有0到了(没有拿任何item)。也可以把别的都初始化成INT_MIN。应该不影响。
这样做最后会TLE,因为每个物品的件数可能很多,循环的常数太大。一个优化是背包九讲中提到的。把物品能取的上限W分成多个“物品”,每个物品的系数分别是1, 2, 4, ..., 2^(k - 1), W - 2^k - 1。其中k是使得W - 2^k - 1>0的最大k。比如13只用试1, 2, 4, 6。虽然转化成了0~1背包,但是原来同一个物品产生出来的这些“物品”都是在前一个“物品”的基础上更新DP的,所以他们之间实质是叠加关系。而这样取系数的方式保证了所有1~W之间的所有数都是能取到的。具体分析见背包九讲。这个优化力度是非常大的。直接就从TLE到了16MS。
for (int i = 0; i < 6; ++i)
{
// for (int w = 1; w <= nums[i]; ++w)
// {
// for (int v = goal; v >= (i + 1) * w; --v)
// {
// dp[v] = max(dp[v], dp[v - (i + 1) * w] + (i + 1) * w);
// }
// }
int W = nums[i];
for (int k = 1; k < W; k <<= 1)
{
for (int v = goal; v >= (i + 1) * k; --v)
{
dp[v] = max(dp[v], dp[v - (i + 1) * k] + (i + 1) * k);
}
W -= k;
}
for (int v = goal; v >= (i + 1) * W; --v)
{
dp[v] = max(dp[v], dp[v - (i + 1) * W] + (i + 1) * W);
}
}
/*
ID: thestor1
LANG: C++
TASK: poj1014
*/
#include <iostream>
#include <fstream>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <climits>
#include <string>
#include <vector>
#include <list>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <algorithm>
#include <cassert>
using namespace std;
// std::vector<std::vector<bool> > cache(60000, std::vector<bool>(6, false));
// bool DFS(int index, int total, const int goal, int nums[])
// {
// <span style="white-space:pre"> </span>if (total == goal)
// <span style="white-space:pre"> </span>{
// <span style="white-space:pre"> </span>return true;
// <span style="white-space:pre"> </span>}
// <span style="white-space:pre"> </span>assert (total < goal);
// <span style="white-space:pre"> </span>if (index < 0)
// <span style="white-space:pre"> </span>{
// <span style="white-space:pre"> </span>return false;
// <span style="white-space:pre"> </span>}
// <span style="white-space:pre"> </span>if (cache[total][index])
// <span style="white-space:pre"> </span>{
// <span style="white-space:pre"> </span>return false;
// <span style="white-space:pre"> </span>}
// <span style="white-space:pre"> </span>cache[total][index] = true;
// <span style="white-space:pre"> </span>for (int i = 0; i <= nums[index]; ++i)
// <span style="white-space:pre"> </span>{
// <span style="white-space:pre"> </span>if (total + i * (index + 1) <= goal)
// <span style="white-space:pre"> </span>{
// <span style="white-space:pre"> </span>if (DFS(index - 1, total + i * (index + 1), goal, nums))
// <span style="white-space:pre"> </span>{
// <span style="white-space:pre"> </span>return true;
// <span style="white-space:pre"> </span>}
// <span style="white-space:pre"> </span>}
// <span style="white-space:pre"> </span>}
// <span style="white-space:pre"> </span>return false;
// }
// int main1()
// {
// <span style="white-space:pre"> </span>int nums[6];
// <span style="white-space:pre"> </span>int seq = 1;
// <span style="white-space:pre"> </span>while (true)
// <span style="white-space:pre"> </span>{
// <span style="white-space:pre"> </span>for (int i = 0; i < 6; ++i)
// <span style="white-space:pre"> </span>{
// <span style="white-space:pre"> </span>cin >> nums[i];
// <span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>
// <span style="white-space:pre"> </span>int sum = 0;
// <span style="white-space:pre"> </span>for (int i = 0; i < 6; ++i)
// <span style="white-space:pre"> </span>{
// <span style="white-space:pre"> </span>// optimize
// <span style="white-space:pre"> </span>// I don't know why
// <span style="white-space:pre"> </span>while (nums[i] > 200)
// <span style="white-space:pre"> </span>{
// <span style="white-space:pre"> </span>nums[i] -= 2;
// <span style="white-space:pre"> </span>}
// <span style="white-space:pre"> </span>sum += nums[i] * (i + 1);
// <span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>
// <span style="white-space:pre"> </span>if (sum == 0)
// <span style="white-space:pre"> </span>{
// <span style="white-space:pre"> </span>break;
// <span style="white-space:pre"> </span>}
// <span style="white-space:pre"> </span>if (sum & 1)
// <span style="white-space:pre"> </span>{
// <span style="white-space:pre"> </span>cout << "Collection #" << seq << ":" << endl;
// <span style="white-space:pre"> </span>cout << "Can't be divided." << endl;
// <span style="white-space:pre"> </span>cout << endl;
// <span style="white-space:pre"> </span>}
// <span style="white-space:pre"> </span>else
// <span style="white-space:pre"> </span>{
// <span style="white-space:pre"> </span>int goal = sum / 2;
<span style="white-space:pre"> </span>
// <span style="white-space:pre"> </span>for (int i = 0; i < goal; ++i)
// <span style="white-space:pre"> </span>{
// <span style="white-space:pre"> </span>for (int j = 0; j < 6; ++j)
// <span style="white-space:pre"> </span>{
// <span style="white-space:pre"> </span>cache[i][j] = false;
// <span style="white-space:pre"> </span>}
// <span style="white-space:pre"> </span>}
// <span style="white-space:pre"> </span>int index = 5;
// <span style="white-space:pre"> </span>while (nums[index] == 0)
// <span style="white-space:pre"> </span>{
// <span style="white-space:pre"> </span>index--;
// <span style="white-space:pre"> </span>}
// <span style="white-space:pre"> </span>assert(index >= 0);
// <span style="white-space:pre"> </span>int total = index + 1;
// <span style="white-space:pre"> </span>nums[index]--;
// <span style="white-space:pre"> </span>if (DFS(index, total, goal, nums))
// <span style="white-space:pre"> </span>{
// <span style="white-space:pre"> </span>cout << "Collection #" << seq << ":" << endl;
// <span style="white-space:pre"> </span>cout << "Can be divided." << endl;
// <span style="white-space:pre"> </span>cout << endl;
// <span style="white-space:pre"> </span>}
// <span style="white-space:pre"> </span>else
// <span style="white-space:pre"> </span>{
// <span style="white-space:pre"> </span>cout << "Collection #" << seq << ":" << endl;
// <span style="white-space:pre"> </span>cout << "Can't be divided." << endl;
// <span style="white-space:pre"> </span>cout << endl;
// <span style="white-space:pre"> </span>}
// <span style="white-space:pre"> </span>}
// <span style="white-space:pre"> </span>seq++;
// <span style="white-space:pre"> </span>}
// <span style="white-space:pre"> </span>return 0;
// }
std::vector<int> dp(60001, 0);
int main()
{
<span style="white-space:pre"> </span>int nums[6];
<span style="white-space:pre"> </span>int seq = 1;
<span style="white-space:pre"> </span>while (true)
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>int sum = 0;
<span style="white-space:pre"> </span>for (int i = 0; i < 6; ++i)
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>cin >> nums[i];
<span style="white-space:pre"> </span>sum += nums[i] * (i + 1);
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>if (sum == 0)
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>break;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>if (sum & 1)
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>cout << "Collection #" << seq << ":" << endl;
<span style="white-space:pre"> </span>cout << "Can't be divided." << endl;
<span style="white-space:pre"> </span>cout << endl;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>else
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>int goal = sum / 2;
<span style="white-space:pre"> </span>fill(dp.begin(), dp.begin() + goal + 1, INT_MIN);
<span style="white-space:pre"> </span>dp[0] = 0;
<span style="white-space:pre"> </span>for (int i = 0; i < 6; ++i)
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>// for (int w = 1; w <= nums[i]; ++w)
<span style="white-space:pre"> </span>// {
<span style="white-space:pre"> </span>// <span style="white-space:pre"> </span>for (int v = goal; v >= (i + 1) * w; --v)
<span style="white-space:pre"> </span>// <span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>// <span style="white-space:pre"> </span>dp[v] = max(dp[v], dp[v - (i + 1) * w] + (i + 1) * w);
<span style="white-space:pre"> </span>// <span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>// }
<span style="white-space:pre"> </span>int W = nums[i];
<span style="white-space:pre"> </span>for (int k = 1; k < W; k <<= 1)
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>for (int v = goal; v >= (i + 1) * k; --v)
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>dp[v] = max(dp[v], dp[v - (i + 1) * k] + (i + 1) * k);
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>W -= k;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>for (int v = goal; v >= (i + 1) * W; --v)
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>dp[v] = max(dp[v], dp[v - (i + 1) * W] + (i + 1) * W);
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>if (dp[goal] == goal)
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>cout << "Collection #" << seq << ":" << endl;
<span style="white-space:pre"> </span>cout << "Can be divided." << endl;
<span style="white-space:pre"> </span>cout << endl;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>else
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>cout << "Collection #" << seq << ":" << endl;
<span style="white-space:pre"> </span>cout << "Can't be divided." << endl;
<span style="white-space:pre"> </span>cout << endl;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>seq++;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>return 0;
}