[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;
}
posted @ 2022-10-06 14:37  Smallbasic  阅读(71)  评论(0)    收藏  举报