202109-2 非零段划分

好像以前做过这个,一找就找到了

https://www.acwing.com/problem/content/submission/code_detail/9763421/

岛屿思想

#include<bits/stdc++.h>
using namespace std;

#define x first
#define y second
#define endl '\n'
#define rep(i,a,n) for (int i = a; i < n; i ++ )
#define repn(i,a,n) for (int i = a; i <= n; i ++ )
#define pb push_back
#define IOS ios::sync_with_stdio(false); cin.tie(0);cout.tie(0);
typedef long long ll;
typedef pair<int,int> PII;

ll gcd(ll a,ll b) { return b ? gcd(b,a % b) : a; }
const int mod = 1e9+7;
const int N = 500010;
int n;
PII q[N];
int h[N];


int main()
{
    IOS;
    cin >> n ;
    for (int i = 1; i <= n; i ++ ) cin >> h[i];
    
    n = unique(h + 1, h + n + 1) - h -1;//unique:去除相邻的重复元素(我们一般使用是先排序但是这题不要!),返回值是最后一个不相同
    //元素的尾地址,所以减去起始地址就是元素的个数了(这里起始位置是1)
    h[n + 1] = 0;//因为下面会用到下标n,还有n + 1,而n + 1的高度不确定
    for(int i = 1 ; i <= n;i++) q[i] = {h[i],i};//pair第一个位置存高度,第二个位置存下标
    
    sort(q + 1, q + n + 1);//排序,pair排序是按第一个元素从小到大
    
    int res = 1, cnt = 1;//定义答案和小山数量,初始时小山全部连在一起,所以是1
    for(int i = 1 ; i <= n ;i++)//从第到高枚举小山
    {
        int k = q[i].y;//读出当前这座小山的下标
        if(h[k - 1] < h[k]&&h[k + 1] < h[k])    cnt--;//只有下面的3,4两种情况小山的数目发生了变化
        else if(h[k - 1] > h[k]&&h[k + 1] > h [k])  cnt ++;
        
        if(q[i].x != q[i + 1].x)//因为unique删除的是相邻的相同高度的小山,如果两个小山高度相同但是隔着距离,那么按上面的判断我们会更
        //新两次,但是由于当前水平面都在同一高度,所以我们不能先更新第一个小山,再更新第二个小山,而应该同时更新。
        //所以要等所有的高度相同的小山都更新完了后,再修改这个最大值res
        res = max(res,cnt);
    }
    cout << res << endl;//小山的数量是题目中的岛屿
/*
首先,对于水位的不断上升,我们没有必要去枚举每一个水位,因为只有当水位到达小山的高度时,小山的状态才会改变。且重合算是被覆盖,
这样最多枚举1e5次,所以统计小山数量只能用O(1)或者O(logn)的算法。而在统计小山的时候,只有水位淹没小山,小山的数量才会改变,
所以我们在枚举的时候考虑数量的变化即可,这就做到了O(n)
考虑小山状态改变对小山数量的影响:1.这座山比左边高比右边矮(左低右矮),当水位到达中间小山高度时,小山的数量并没有改变。
2.左高右低,小山数量同样没有改变3.中高两侧低,小山数量-1。 4.中低两侧高,数量+1。当然有特殊情况,比如左=中<右,那么由于左的左边的
情况未知,所以无法判断了?那么如果有很多相同的小山,枚举的复杂度就成了O(n),当然也就过不了最坏的情况。那么怎么处理,如果n个山初始的
时候高度一样,那么我们就直接当成一个山就好了(这些山要么一起被淹没,要么保持原状态)
所以我们把这些小山当成一个,即在初始的时候(读入完)对小山进行一个判重,注意不要先排序
枚举的时候高度需要从上往下枚举,所以需要排序O(nlogn)
*/
    
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值