Acwing2014. 岛
题目
每当下雨时,农夫约翰的田地总是被洪水淹没。
由于田地不是完全水平的,所以一些地方充满水后,留下了许多被水隔开的“岛”。
约翰的田地被描述为由 N 个连续高度值 H1,…,HN 指定的一维场景。
假设该场景被无限高的围墙包围着,请考虑暴雨期间发生的情况:
最低处首先被水覆盖,形成一些不连贯的岛,随着水位的不断上升,这些岛最终都会被覆盖。
一旦水位等于一块田地的高度,那块田地就被认为位于水下。
上图显示了一个示例:在左图中,我们只加入了刚好超过 11 单位的水,此时剩下 4 个岛(最大岛屿剩余数量),而在右图中,我们共加入了 7 单位的水,此时仅剩下 2 个岛。
请计算,暴风雨期间我们能在某个时间点看到的最大岛屿数量。
水会一直上升到所有田地都在水下。
输入格式
第一行包含整数 N。
接下来 N 行,每行包含一个整数表示 Hi。
输出格式
输出暴风雨期间我们能在某个时间点看到的最大岛屿数量。
数据范围
1≤N≤105,
1≤Hi≤109
输入样例:
8
3
5
2
3
1
4
2
3
输出样例:
4
为什么想到用差分(个人认为很重要)
AcWing 2014. 岛深入剖析差分的本质探究差分解法的由来 - AcWing
思路
设一块长方形的高度为a[i],假设水从a[i-1]涨到(下降到)a[i],意味着所有高度在a[i]—a[i-1]的长方形都会被淹没(露出来),相当于这些长方形的高度差被抹平(产生)(对答案的贡献都减1(+1)).同时给一个区间减去(加上)一个数,用差分
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 500005,M = 10000;
int a[N],b[N];
int n;
int main()
{
cin >> n;
int res = 0;
for (int i = 1; i <= n; i ++ ){
cin >> a[i];
if(a[i]>a[i-1]){
//数的大小在[a[i-1],a[i]-1]之间的所有数都+1
b[a[i-1]]++,b[a[i]]--;
}
}
int sum = 0 ;
for (int i = 0; i <= M; i ++ ){
sum+=b[i];
res = max(res,sum);
}
cout << res;
}
题解
对比4007. 非零段划分 - AcWing题库发现高度的数据范围比数量大的多余数想到了用map
离散化(不能用unordered_map
,unordered_map
无法用来求前缀和
)
代码:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <map>
typedef long long LL;
using namespace std;
const int N = 100005,M = 1e9+1;
int a[N];
map<int ,int >b;
int n;
int main()
{
cin >> n;
for (int i = 1; i <= n; i ++ ){
cin >> a[i];
if(a[i]>a[i-1]){
//数的大小在[a[i-1],a[i]-1]之间的所有数大小都+1
b[a[i-1]]++,b[a[i]]--;
}
}
LL sum = 0 ,res = 0;
for (auto i:b ){
//求前缀和
sum+=i.second;
res = max(res,sum);
}
cout << res;
}
这段代码确实比手写离散化要简洁很多,非常棒!!!但是我一开始并没有完全理解其中的真意,经过一番理解之后,再此提供一段”描述”,希望能作为这篇博客的补充!!!
1. 为什么能用差分? 考虑水平面不断升高的过程:以一种情况,在水平面处于a[i - 1]到a[i] - 1之间的时候,a[i]一定会是一个岛;第二种情况,在水平面低于a[i - 1]的时候a[i - 1]和a[i]一起构成一个岛,我们此时把形成这个岛的功劳归给a[i - 1];第三种情况,水平面高于a[i]的时候,a[i]无法形成岛.所以综上,a[i]对形成岛屿有贡献当且仅当水位是出于a[i - 1]到a[i] - 1之时(能贡献一个).所以我们只需要扫描所有的a[i],对每一个a[i]能贡献的区间加上1后,就能得到一个在不同的水位时的贡献图.自然而然的我们就能知道在什么水位的时候贡献最多(也就是岛屿最多).使用差分也仅仅是优化了这个对区间加1的过程.
2. 为什么只要使用a[i] > a[i-1]的时候才算入是a[i]的贡献? a[i] > a[i + 1]的时候呢? 其实后者是多余的,因为一个地点能被称为岛屿也就说明其两侧水位必定小于岛屿的高度, 这是一个1对2的关系,考虑一侧就行,但是如果都考虑应该也行,只是感觉容易产生边界错误,并且贡献都成了两倍,结果要记得除二.
以上是个人理解,希望给不理解的同学提供帮助!!!
评论没法点赞,只能手动点赞了 👍
👍
👍
讲的太棒了!👍
👍
👍
👍
👍
👍
👍
👍
👍
👍
还有一种理解关于为什么对于1到n只用a[i-1]<a[i], 把未遍历过的所有a[i]看作一个大岛,再在之后的遍历中把满足条件突出的小岛单独分割出并且对应相应水位。
突然想到个问题,a[i]的值最大能到10^9,b数组会爆吧
没事了我刚刚看错了
蒟蒻想问一下为什么是 +=i.second 这里b中的first second代表什么意思呢
不是键值对吗?first是键,second是值
对答案的贡献都+1应该怎样理解?
#同问
可理解成,从某高度划一刀过去,可切下一个山头。也就是说在这高度区间(注意是高度区间)任一高度划一刀过去,皆可切下一个山头,即为此区间”对答案贡献皆为+1”
实际上就是只看连续上升的段(或者只看连续下降的段),做一次扫描线求最大重叠
大佬总结的好,恍然大悟,感谢
应该是数的大小在[a[i-1],a[i]-1]之间的所有数的值都 +1 才对
感谢纠正,已更正~