2024/9 4-8 笔记
[CCO2017] 接雨滴
题目描述
晚上,夜黑风高,大雨疯狂地从天而降。
Lucy 想要接住一些雨滴,但她只有有限的工具。她有一套不同高度的柱子来接住雨滴。每根柱子的高度为整数,宽度为 \(1\)。她排列好柱子之后,就会用其他器具夹紧柱子,来让雨滴顺利地储存在柱子的间隙里。你可以认为雨滴的数量是无限的。
举个例子,如果 Lucy 有高度分别为 \((1,5,2,1,4)\) 的五根柱子,她可以这样排列柱子。
*
* *
* *
** *
*****
这样会接住 \(5r\) 雨滴(\(r\) 代表 \(1\) 个单位的雨滴)。
为了方便表述,我们定义 \(r\) 为雨滴的单位。
*
*RR*
*RR*
**R*
*****
当然了,她也可以这样摆放柱子,这样可以接住 \(6r\) 雨滴。
*
*RR*
*RR*
**RR*
*****
再举一个例子,如果柱子的高度分别为 \((5,1,5,1,5)\),Lucy 可以接住 \(8r\) 雨滴。
*R*R*
*R*R*
*R*R*
*R*R*
*****
最后一个例子,如果柱子的高度分别为 \((5,1,4,1,5)\),她可以接住 \(9r\) 雨滴。
*RRR*
*R*R*
*R*R*
*R*R*
*****
Lucy 有 \(n\) 个高度为 \(h_1,h_2,...,h_n\) 的柱子。她想知道,在所有可能的摆放方案中,所有可能的雨滴量(以 \(r\) 为单位)是多少。(具体可看样例解释)
slove
结论:对于某个柱子 x 上方的积水体积,可能产生的所有体积都是合法的。
证明:
首先,形成积水肯定需要两个高柱子,即最高柱和次高柱。我们先放好。接着,我们要插入一个柱子\(x\),考虑\(x\)上方能增加多少积水体积。
1)若我们想让x上方没有积水
我们找到柱子\(y\),使得\(Hy <= Hx\),\(Hy\)最小并且y上方没有积水。
我们再找一个比\(y\)更高的柱子\(z\)
https://excalidraw.com/
将\(x\)插在\(y\)和\(z\)之间且紧贴着\(z\)。
因为我们找到的 \(y\) 是所有高度不大于 \(x\) 号柱子、且上方没有积水的柱子里最低的那一个,因此 \(z\) 高度一定大于等于 \(x\) (不然找到的就该是 \(z\) 了),于是这么插入不会产生新的积水。
在\(y\)存在的情况下,由于有最高峰,所以\(z\)一定存在。如果y不存在,那么不被积水覆盖的柱子都比x高,直接把x放在边界。
因此,一定有一种方案使得插入x后总共先不变。
2)设有一个比他高的柱子\(y\),要使得总贡献增加\((a{_y}-a{_x})\)
1.如果y没有被积水覆盖
除非\(y\)是最高峰,一定可以插到y的一侧满足总贡献增加\((a{_y}-a{_x})\)
2.如果\(y\)被覆盖了。
找到一个离\(y\)最近的,最低的且没有被积水覆盖的柱子\(z\).
我们把\(y\)换为\(x\),贡献变为了\((a{_y}-a{_x})\), 把\(y\)抽出来,按照1)处理即可。
3)如果\(y\)未被插入,从大到小插入就可以。
综上,对于某个柱子 $
x $上方的积水体积,可能产生的所有体积都是合法的。
背包转移,bitset优化。
using namespace std;
const int maxN=5e2+5,maxH=5e1+5;
int a[maxN];
bitset<maxN*maxH>dp,tem;
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;++i)
{
scanf("%d",&a[i]);
}
sort(a+1,a+1+n);
dp[0]=true;
for(int j=1;j<n;++j)
{
for(int st=j+1;st<n;++st)
{
tem|=(dp<<(a[st]-a[j]));
}
dp|=tem;
}
for(int i=0;i<=25000;++i)
{
if(dp[i])
{
printf("%d ",i);
}
}
}