洛谷 : P5788 【模板】单调栈
众所周知
有了神就有了光,有了梦想就有了动力,有了lbw就有了牛,咳咳, 有了单调队列就有单调栈。
如果你还不了解单调队列,可以前往->我的算法不可能这么简单—单调队列
诶,读完题(这不是个大水题吗,看我直接干了它)
?n<=3×106 打扰了打扰了。。。
先把暴力思想水了,然后与单调栈进行比较一下:
#include
using namespace std;
#define int long long
int a[3000006],n;
signed main(){
scanf("%lld",&n);
for(int i=1;i<=n;++i)
scanf("%lld",&a[i]);
for(int i=1;i<=n;++i){
bool flag = false;
for(int j=i+1;j<=n;++j)
if(a[j]>a[i]){
printf("%lld ",j);
flag=true;
break;
}
if(!flag) printf("0 ");
}
return 0;
}
结合单调队列,我们可以很快知道这玩意应该存的也是一个单调递增或者单调递减的值的下标 。
这题是单调递增栈。。从栈顶往栈底看是单调递增的。既然如此,我们仍然跟单调队列一样,采用数组模拟栈:
int Stack[3000006],top;//Stack为我们的栈,top代表栈顶
我们先画个图分析一下:
- 我们从左往右看,1的第一个最大值显然是4,如果我们让 1 先进栈,那么下一个进栈的4会将1顶出去。所以。。。
- 所以我们怎么判断1后面第一个大于1的元素值是4的?好像并不能判断。。。
- 既然从左到右看,没法干成,那么我们从右往左看一下试试。
- 我们先让5进栈,下一个元素3,发现当前栈顶是5,而且5正好是3后面的第一个大值。
- 我们让3进栈,下一个元素2,发现当前栈顶是3,而且3正好是2后面的第一个大值。
- 我们让2进栈,下一个元素4,4比栈顶的2大,我们让2出栈,栈顶变成3,依旧让3出栈,然后栈顶变成5,4比5小,而且4后面的第一大值就是当前栈顶5!
- 最后我们让4进栈,下一个元素1,显然1后面的第一大值就是当前栈顶4!!!
我们以图的形式再次形象的分析一遍上述过程,因为这个过程就是代码的实现步骤!
5是最后一个元素了诶,当前栈为空,那么我们直接记录栈顶元素0,5入栈。
经过上面的分析,我相信代码的实现已经不是困难的事情
#include
using namespace std;
#define int long long
int a[3000006],n;
int Stack[3000006],top;
int ans[3000006];//记录答案
signed main(){
scanf("%lld",&n);
for(int i=1;i<=n;++i)
scanf("%lld",&a[i]);
//从右往左看 从n到1
for(int i=n;i>=1;--i){
//如果新元素比栈顶元素大,弹出栈顶元素
while(top && a[Stack[top]]<=a[i]) top--;
//记录当前栈顶元素
ans[i] = Stack[top];
//新元素的下标入栈
Stack[++top] = i;
}
for(int i=1;i<=n;++i)
printf("%lld ",ans[i]);
return 0;
}
我们其实可以发现,单调栈的代码非常非常非常短。。。然后就绿了这道题。
P2947 [USACO09MAR]Look Up S
#include
using namespace std;
#define int long long
const int maxn = 1e5+9;
int a[maxn],n;
int Stack[maxn],top;
int ans[maxn];
signed main(){
scanf("%lld",&n);
for(int i=1;i<=n;++i)
scanf("%lld",&a[i]);
for(int i=n;i>=1;--i){
while(top&&a[Stack[top]]<=a[i]) top--;
ans[i] = Stack[top];
Stack[++top] = i;
}
for(int i=1;i<=n;++i)
printf("%lld\n",ans[i]);
return 0;
}
P1901 发射站
#include
using namespace std;
#define int long long
const int maxn = 1e6+9;
int h[maxn],v[maxn],n;
int Stack[maxn],top;
int ans[maxn];
signed main(){
scanf("%lld",&n);
for(int i=1;i<=n;++i)
scanf("%lld %lld",&h[i],&v[i]);
//本题正着,倒着都可以
for(int i=n;i>=1;--i){
//如果新元素比栈顶元素大,说明这个信号塔把这个信号塔的信号接收了
while(top && h[Stack[top]]<=h[i]) ans[i]+=v[Stack[top--]];
//遇到了栈顶元素,此时栈顶元素要么比该信号塔高接受了该信号塔的信号
//要么就是该元素为栈顶,它的信号没有被任何塔接受到,直接存放到0号位置,不影响结果,因为我们是从1开始的
ans[Stack[top]] += v[i];
Stack[++top] = i;
}
int res = 0;
for(int i=1;i<=n;++i)
res = max(ans[i],res);
cout<<res;
return 0;
}
P2866 [USACO06NOV]Bad Hair Day S
#include
using namespace std;
#define int long long
const int maxn = 1e5+9;
int a[maxn],n;
stack<int> s;
int ans[maxn];
signed main(){
scanf("%lld",&n);
for(int i=1;i<=n;++i)
scanf("%lld",&a[i]);
for(int i=1;i<=n;++i){
while(!s.empty() && a[s.top()]<=a[i]) s.pop();
if(!s.empty()) ans[s.top()] += s.size();
s.push(i);
}
int res = 0;
for(int i=1;i<=n;++i)
res += ans[i];
cout<<res;
return 0;
}