近来往往看到过一种类型的贪心的题,就是那种不明所以却有感觉好神奇啊的那种题。
直接上例题。
AtCoder 064 D
那么就来想一想这道题的做法,一开始以为是道栈的题,但发现栈底无法处理,那么就双向队列吧,但发现中间无法插入,那么就splay的合并分裂吧,那么最小字典序呢?拜托,这是beginner的难度。。。
思考了一会儿,
对于一串括号,我只需要正着搜索,反着搜索不就行了吗?
每次记录右减左与左减右的max再printf就可以了吧。。。
naive code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<stack>
#include<algorithm>
using namespace std;
string st;
stack<int> sa;
int a[1000],sum[1000],len=1,cnt=0,rd=0,ld=0,l=0,r=0;
int n,flag=-1;
int main(){
cin>>n>>st;
for(register int i=0;i<n;i++){
if(st[i]=='(')a[i]=1;
else a[i]=0;
}
for(register int i=0;i<n;++i){
if(a[i]==1)l++;
else r++;
rd=max(rd,r-l);
}
l=r=0;
for(register int i=n-1;i>=0;--i){
if(a[i]==1)l++;
else r++;
ld=max(ld,l-r);
}
for(register int i=1;i<=rd;++i)cout<<'(';
for(register int i=0;i<n;++i)cout<<st[i];
for(register int i=1;i<=ld;++i)cout<<')';
return 0;
}
由此引入我所谓的高山算法。
为什么叫做高山算法?因为每走一步都要测量一下,当前维护信息的值,而最后所需的值并不一定是末尾所处理的值,而是过程中某一处的特值,一般是最大最小值。
Q:为什么叫做高山算法?
A:这是模拟爬山的测量的一个过程,更类似于扫描线,而已经有爬山这个算法了,所以就叫高山算法吧。
Q;这个算法有什么用?
A:其实并没有什么意思,只是提出这样一个类扫描线的贪心算法,有助于理解
O(n)
复杂度类寻找简单类型的查询问题。
Q:还有什么例题?(该不会是你一时兴起决定的吧?)
A:下方(是的)。
例一 Maximum Sum Sequence
好吧,确实是道naive的题,秒了。代码Leaderboard中有。
例二 Maximum Sum Sequence II
这道题就有意思了吧,还是高山算法,如何做?
其实加个预处理就好了。。。
代码还是见Leaderboard。
LAST ONE 例三
Largest Rectangle in Histogram
之所以不发链接,是因为这道题蛮经典的,到处到可测,例如SPOJ。
这是体现出高山算法在贪心算法中都有无与伦比的“脑洞值”的优秀题目,所以这道题值得一做。
问题描述:给定一个长度为n的直方图,我们可以在直方图高低不同的长方形之间画一个更大的长方形,求该长方形的最大面积。例如,给定下述直方图,
我们可以以高度5宽度2画一个更大的长方形,如下图,该长方形即是面积最大的长方形。该问题是难度比较大的问题,但是很出名,经常作为面试题出现。用高山算法可以做到O(n)的复杂度,非常巧妙,并从二维三维角度对问题进行了扩展。
仔细思考吧,毕竟高山算法其实挺简单的。
高山==单调队列/单调栈?
不是这样,单调队列需要维护一整个队列的单调性,单调栈同理,而高山算法不改变遍历时的处理顺序,不用额外的数据结构存储,是一个面向答案编写程序的算法。
总结
没什么好说的,这样一个奇妙的算法究竟能够延展到什么程度?静待吧。