2022.01.06 Acwing寒假每日一题 岛

1、题目

每当下雨时,农夫约翰的田地总是被洪水淹没。
由于田地不是完全水平的,所以一些地方充满水后,留下了许多被水隔开的“岛”。
约翰的田地被描述为由 N 个连续高度值 H1,…,HN 指定的一维场景。
假设该场景被无限高的围墙包围着,请考虑暴雨期间发生的情况:
最低处首先被水覆盖,形成一些不连贯的岛,随着水位的不断上升,这些岛最终都会被覆盖。
一旦水位等于一块田地的高度,那块田地就被认为位于水下。

在这里插入图片描述

上图显示了一个示例:在左图中,我们只加入了刚好超过 1 单位的水,此时剩下 4 个岛(最大岛屿剩余数量),而在右图中,我们共加入了 7 单位的水,此时仅剩下 2 个岛。

请计算,暴风雨期间我们能在某个时间点看到的最大岛屿数量。
水会一直上升到所有田地都在水下。

输入格式
第一行包含整数 N。

接下来 N 行,每行包含一个整数表示 Hi。

输出格式
输出暴风雨期间我们能在某个时间点看到的最大岛屿数量。

数据范围
1≤N≤10^5,
1≤Hi≤10^9
输入样例:
8
3
5
2
3
1
4
2
3
输出样例:
4

2、分析

首先,我们发现,不需要一个单位一个单位向里面倒水去判断有几个岛。
在这里插入图片描述
我们只需要按照上面这样,对于每个块的高度进行分析就可以。因为其他的高度对于岛的数量不构成影响。
而此时的时间复杂度为 O ( n ) O(n) O(n),数据量是 1 0 5 10^5 105,这就意味着求岛的数量不可以再遍历了,否则会超时。
我们观察,对于每根柱子,其和左右的关系共有下面四种形状。
在这里插入图片描述
对于第一种形状,如果被淹没了,那么岛的数量会增加1。
对于第二种形状,如果被淹没了,那么岛的数量会减少1。
对于后两种形状,岛的数量不变。
所以我们只要维护一个全局变量res,然后在遍历每个块的时候,就可以根据其左右状态直接得到当前岛的数量了。

那么问题来了,你怎么不列上这样的形状呢?
在这里插入图片描述
对于连续的相同高度的柱子,可以视为一根柱子,因为对于这些柱子的左右柱子来说,他们是同时被淹没,对于岛的数量的贡献是同时的。压缩过连续相同高度的柱子之后,就只剩上面的四种情况了。

接下来还有个细节。就是更新res的时候,一定要存储一个临时变量cnt,将这个高度的所有块的贡献或者减少的岛全部计算完之后,再更新res。具体见代码。

3、代码

#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int N = 1e5 + 10;
int h[N];
pair<int,int> p[N];
int main()
{
    int n;
    cin >> n;
    //从1开始,方便处理最左边的岛
    for(int i = 1; i <= n; i ++) cin >> h[i];
    //去掉连续并且高度相同的块,因为在淹没的时候,他们地位相同,所以合成一个处理
    n = unique(h + 1, h + n + 1) - h - 1;
    //避免后面h[k] 与 h[k + 1]比较时出错
    h[n + 1] = 0;
    for(int i = 1; i <= n; i ++) p[i] = {h[i],i};
    //因为对于相同的高度需要同时被淹没,排序后比较方便对相同高度的块进行同时处理
    sort(p + 1,p + n + 1);
    //最开始所有的块连成一个大岛,所以从1开始
    int res = 1,cnt = 1;
    for(int i = 1; i <= n; i ++)
    {
        int k = p[i].second;
        //对于两种变化的状态更新cnt,cnt代表当前岛的数量
        if(h[k] < h[k - 1] && h[k] < h[k + 1] ) cnt ++;
        if(h[k] > h[k - 1] && h[k] > h[k + 1]) cnt --;
        //当前高度的所有块被淹没时对于岛数量的影响已经结束,更新淹没当前高度的岛的数量
        if(p[i].first != p[i + 1].first) res = max(cnt,res);
    }
    cout << res;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值