Description
莉婕喜欢做饼干。她制作了 N 种饼干。第 i 种饼干有 Ai 个。为了出售她制作的饼干,她将它们装入盒子中。但是,应该满足以下条件。
对于每个盒子,其中的饼干种类应不同。
对于每个盒子,其中的饼干数量应等于以下 M 个数字之一:B1,B2,⋯ ,BM。
编写一个程序,给出莉婕制作的饼干信息和将饼干装箱的条件,确定是否可能将所有饼干包装到盒子中。此外,如果可以将所有饼干包装在盒子中,则您的程序应输出最少的盒子数量。
1≤M≤N≤15000,∑Ai≤15000。
Solution
假设有 k 个盒子,Ci 表示第 i 个盒子的大小,考虑怎么判定一组 Ci 合法。
首先 ∑Ci=∑Ai。
然后把 Ci 从大到小排序,那么前 j 个盒子需要放 ∑i=1jCi 个饼干,而每种饼干在每个盒子最多放一个,所以需要满足 ∑i=1jCi≤∑i=1nmin{Ai,j}。
这个东西是必要条件,下面用 Hall 定理证明这个也是充分的。
首先建出二分图,左部点是盒子,右部点是饼干,S 向第 i 个盒子连流量 Ci 的边,第 i 种饼干向汇点连流量 Ai 的边,第 i 个盒子向第 j 种饼干连流量为 1 的边。
根据 Hall 定理,对于任意盒子的子集 S,需要满足 ∑i∈SCi≤∑j=1nmin{Aj,∣S∣},最劣情况一定是从大到小排序后的一段后缀,即上面的结论。
然后就可以 dp 了。
设 fi,j,k 表示已经考虑了前 i 大的盒子,选了 j 个,总和为 k 是否可行。
用 bitset 转移是 O(wS3) 的,但是由于 j≤BiS,所以总状态是 SlogS 级别,复杂度优化为 O(wS2logS)。
时间复杂度:O(wS2logS)。
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
| #include <bits/stdc++.h>
const int kMaxN = 1.5e4 + 5;
int n, m, mm, s; int a[kMaxN], b[kMaxN], c[kMaxN], lim[kMaxN], coef[kMaxN]; std::vector<std::bitset<kMaxN>> f[kMaxN]; std::bitset<kMaxN> g[kMaxN];
void getcc(int i, int j, int k) { if (i == m + 1) return assert(!j && !k); while (1) { if (j < f[i + 1].size() && f[i + 1][j][k]) return getcc(i + 1, j, k); c[++mm] = b[i], --j, k -= b[i]; } }
void dickdreamer() { std::cin >> n; for (int i = 1; i <= n; ++i) { std::cin >> a[i]; ++coef[1], --coef[a[i] + 1], lim[a[i] + 1] += a[i]; s += a[i]; } std::cin >> m; for (int i = 1; i <= m; ++i) std::cin >> b[i]; for (int i = 1; i <= s; ++i) { lim[i] += lim[i - 1], coef[i] += coef[i - 1]; } for (int i = 0; i <= s; ++i) { lim[i] += i * coef[i]; g[i].reset(), g[i].flip(); g[i] ^= (g[i] << (lim[i] + 1)); } f[m + 1].resize(1), f[m + 1][0].reset(), f[m + 1][0][0] = 1; for (int i = m; i; --i) { f[i].resize(s / b[i] + 1); for (int j = 0; j < f[i].size(); ++j) f[i][j].reset(); for (int j = 0; j < f[i + 1].size(); ++j) f[i][j] = f[i + 1][j]; for (int j = 0; j < f[i].size(); ++j) { f[i][j] &= g[j]; if (j + 1 < f[i].size()) f[i][j + 1] |= (f[i][j] << b[i]); } } int cnt = -1; for (int i = 0; i < f[1].size(); ++i) { if (f[1][i][s]) { cnt = i; break; } } if (!~cnt) return void(std::cout << "-1\n"); getcc(1, cnt, s); std::cout << cnt << '\n'; std::priority_queue<std::pair<int, int>> q; for (int i = 1; i <= n; ++i) q.emplace(a[i], i); for (int i = 1; i <= cnt; ++i) { std::vector<int> vec; for (int j = 1; j <= c[i]; ++j) { vec.emplace_back(q.top().second); q.pop(); } std::cout << c[i] << ' '; for (auto x : vec) { std::cout << x << ' '; --a[x]; assert(a[x] >= 0); if (a[x]) q.emplace(a[x], x); } std::cout << '\n'; } }
int32_t main() { #ifdef ORZXKR freopen("in.txt", "r", stdin); freopen("out.txt", "w", stdout); #endif std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0); int T = 1; while (T--) dickdreamer(); return 0; }
|