非零段划分
题目 非零段划分
思路分析
可以分三个方向考虑
(1):y总那样的 水从下往上涨 去分析每个点被淹没时 答案的变化
(2):反过来 水一开始在最高位 往下退 分析每个点露出时 答案的变化
(3):从每个点入手 依次加入每个点 利用差分的思想 分析答案变化
对于第一个方向
我们可以发现 对于上涨后淹没的每个点 不外乎就4种状态
1.单调上升的(左边比它矮 右边比它高) 那么把它淹没 还是有一个岛(它右边的)只不过体积变小了而已 对答案没影响
2.单调下降的(左边比它高 右边比它矮)那么把它淹没 也是一样 还有左边一个岛 也只是左边这个岛的体积变小了罢了
3.突出(左右都比他矮) 那么把它淹没 原本它是一座岛 现在没了 答案-1
4.凹陷(左右都比他高) 那么把它淹没 原本连成一块的岛就变成了两块 答案+1
对于一种特殊情况
就是如果有很多相邻的 且高度一样的岛 我们无法通过上诉的只比较当前点与左右点来得到
其实可以发现 这些相邻的 高度一样的岛 完全可以看成一个岛 他们共进退、
那么就可以用unique进行一个去重
(unique只是去重相邻元素 不要误解 先sort再unqiue才会把所有重复去除 所以它完美适应该场景)
那么 就可以处理每个点 通过判断它与相邻点的关系
来去对初始状态进行++或--
最后求一次前缀和 在这个过程中用max维护一个最大值 就能得到答案
但是我们可以发现一个问题
初始条件是什么?
理想情况下 当水没涨时 所有的点都在水平面上 形成一个岛 是吧
但是 如果000呢 那不就是0座岛吗 101呢 两座?
这样去写的话 我们还得去对初始状态有个判断 太麻烦了
那么看第二种方式
水平面一开始在最高处 把所有的点都淹没 这样一来 初始状态就确定了 初始为0座岛
好像省事了很多哈
再往下分析
发现也不过是分为4种情况
1.单调上升的(左边比它矮 右边比它高) 那么当它暴露出来 还是有一个岛(它右边的)只不过体积变大了而已 对答案没影响
2.单调下降的(左边比它高 右边比它矮)那么把它暴露出来 也是一样 还有左边一个岛 也只是左边这个岛的体积变大了罢了
3.突出(左右都比他矮) 那么把它暴露出来 原本左右分割的两个岛 现在被它连在一起了 答案-1
4.凹陷(左右都比他高) 那么把它露出 原本没有的岛 现在变成了一座 答案+1
发现其实就是和前一种++,-- 颠倒一下罢了
然后最后的答案应该是求一遍后缀和 (从最后一位开始算 累加起来 过程中找max)
接下来看第三种方式
它利用差分的思想 依次看加入每个点后 对答案的影响
这种比较难想
首先对于第一个0 其实不需要处理
对于9
可以发现 9一加入的话 任何0~9的水平面都会有一个岛
再看下一个2 它是往下的
对答案并不构成影响 在0~9的水平面还是只有一个岛露出
再看下一个6
可以发现 若水平线在2~6 就会加一个岛露出
而0~2 6~9处并不受影响 还是由前面的1影响
我们可以发现一个规律 若出现该元素比上一个元素大 a[i]>a[i-1](呈单增趋势)时
答案会被影响 即出现一个新的峰 影响怎么表示呢 其实就是 在a[i-1]到a[i]处全加上一个1
那不就是差分吗
只需要在插入每个点的时候 判断与前一个点的关系
如果大于 就在差分数组的b[a[i-1]]处++ b[a[i]]处--即可(无需+1 也是左闭右开)
不信可以继续往后验证
我们可以把它抽象成这样:
得到一个差分数组
重新构造前缀和后 再维护找到一个max 那就是答案
代码实现
水面上涨
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
int n;
int h[N];
int cnt[10010];
int main()
{
cin>>n;
int sum=0;
for(int i=1;i<=n;i++){
cin>>h[i];
if(h[i])
sum=1;
}
n=unique(h+1,h+n+1)-(h+1);
h[0]=1,h[n + 1] = 0;
for(int i=1;i<=n;i++){
if(h[i-1]<h[i] && h[i+1]<h[i])
cnt[h[i]]--;
else if(h[i-1]>h[i] && h[i+1]>h[i])
cnt[h[i]]++;
}
//除了特殊的一开始就全0的情况 不管水平面涨不涨都没岛
//否则默认所有的山都露在水面上 为一座岛
//这里不好处理 得重新遍历 不妨放在读入时处理
int res=0;
for(int i=0;i<10000;i++){
sum+=cnt[i];
res=max(res,sum);
}
cout<<res<<endl;
return 0;
}
//发现这种从下往上找 初始状态都不方便确定
//其实如果出现010这样的数据 感觉这个方法还是会出现问题
//那就是一开始初始为两个岛咯
//很麻烦
水面下降
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
int n;
int h[N];
int cnt[10010];
int main()
{
cin>>n;
for(int i=1;i<=n;i++){
cin>>h[i];
}
n=unique(h+1,h+n+1)-(h+1);
h[0]=0,h[n + 1] = 0;
for(int i=1;i<=n;i++){
if(h[i-1]<h[i] && h[i+1]<h[i])
cnt[h[i]]++;
else if(h[i-1]>h[i] && h[i+1]>h[i])
cnt[h[i]]--;
}
int sum=0,res=0;
//构造后缀和
for(int i=10000;i>=0;i--){
sum+=cnt[i];
res=max(res,sum);
}
cout<<res<<endl;
return 0;
}
从点入手 差分
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
int n;
int h[N],b[N];
int main()
{
cin>>n;
for(int i=1;i<=n;i++){
cin>>h[i];
if(h[i]>h[i-1]){
b[h[i-1]]++,b[h[i]]--;
}
}
int sum=0,res=0;;
for(int i=0;i<=10000;i++){
sum+=b[i];
res=max(res,sum);
}
cout<<res<<endl;
return 0;
}
同类题型
视频讲解
⬅️ 金发姑娘和 N 头牛 🏠 00-刷题理模型 ➡️ _int128
💬 评论