[CCO2017] 接雨滴
晚上,夜黑风高,大雨疯狂地从天而降。
Lucy 想要接住一些雨滴,但她只有有限的工具。她有一套不同高度的柱子来接住雨滴。每根柱子的高度为整数,宽度为 \(1\)。她排列好柱子之后,就会用其他器具夹紧柱子,来让雨滴顺利地储存在柱子的间隙里。你可以认为雨滴的数量是无限的。
举个例子,如果 Lucy 有高度分别为 \((1,5,2,1,4)\) 的五根柱子,她可以这样排列柱子。
*
*rr*
*rr*
**r*
*****
这样会接住 \(5r\) 雨滴(\(r\) 代表 \(1\) 个单位的雨滴)。
为了方便表述,我们定义 \(r\) 为雨滴的单位。
Lucy 有 nn 个高度为 \(h_1,h_2,...,h_n\) 的柱子。她想知道,在所有可能的摆放方案中,所有可能的雨滴量(以 \(r\) 为单位)是多少。(具体可看样例解释)
Sol:
考虑结论,对于某个柱子 \(x\) 上方的积水体积,可能产生的所有体积都是合法的。证明考虑从大到小插入。
先考虑没有积水的情况,找到一个高度小于 \(h_x\) 的 没有积水的柱子 \(y\),再找到高度大于 \(y\) 的没有积水的柱子 \(z\),把 \(x\) 插入 \(z\) 靠 \(y\) 的一侧。
有积水的情况,假设我们要让它积出 \(h_y-h_x\) 的水,或者说使得当前的总答案增加 \(h_y-h_x\) (注意 \(h_y\) 不能是最大值,否则无法产生这种情况)。若 \(y\) 上没有积水,直接把 \(x\) 插到 \(y\) 和最大值之间,否则用 \(x\) 替换 \(y\),并把 \(y\) 用没有积水的情况处理,这样答案增加的也是 \(h_y-h_x\)。
直接跑背包,用 bitset 优化即可。
#include <bits/stdc++.h>
using namespace std;
const int N = 505;
bitset<N * 50> f, ans;
int n, a[N];
inline int read() {
register int s = 0; register char ch = getchar();
while (!isdigit(ch)) ch = getchar();
while (isdigit(ch)) s = (s << 1) + (s << 3) + (ch & 15), ch = getchar();
return s;
}
int main() {
n = read();
for (int i = 1; i <= n; ++i) a[i] = read();
sort(a + 1, a + n + 1); ans[0] = 1;
for (int i = 1; i < n; ++i) {
for (int j = i + 1; j < n; ++j)
f |= ans << (a[j] - a[i]);
ans |= f;
}
for (int i = 0; i < N * 50; ++i)
if (ans[i]) printf("%d ", i);
return 0;
}