题意:找到每个数后面第一个大于它的元素的下标
可以比喻成站了一队人,每个人向右看,求他们每个人看到的第一个人的位置
顾名思义,单调栈就是栈内元素具有单调性,其维护的就是一个数前/后第一个大于/小于他的数
我们将输入的数列a[]倒着遍历一下(要找每个数后面的第一个比它大的数,从后向前入栈可以实现),入栈的第一个数也就是数列的最后一个,它的对应函数值一定为0。倒数第二个入栈,和栈顶元素即它后面元素(即最后一个元素)相比,若比它大,栈顶元素就出栈,否则这个倒数第二个元素就正常入栈......
有一个问题是,这样就能保证求出的是第一个大于的数吗?
假设我们已经求出了ai的函数值fi,对于aj(aj<=ai)来说(只需考虑 j<i 的情况),ai一定不会对f1、f2、...、fj有影响,可能i对于aj是一个解,但也许在1...j-1中有更好的解。也就是O(n^2)暴力枚举的思想,找到一个比它大的数就退出;于是,我们对于整个序列都用贪心维护一个栈保存可能有用的元素下标,若栈顶对应元素比当前元素不大,贪心,然后弹出栈顶的下标。直到栈顶对应元素比当前元素大了,就说明不能继续贪心了,记录答案(重点,最靠前可行的元素就是栈顶对应的元素)。最后将当前下标加入栈。
#include <iostream>
#include <cstdio>
#include <stack>
#include <algorithm>
using namespace std;
const int maxn=3000001;
int a[maxn],f[maxn];
int main()
{
int n;
//cin>>n;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]); //cin>>a[i];
stack<int> s;
int i=1;
/*bool flag;//true means no one bigger than itself
while(i<=n)
{
flag=true;
s.push(a[i]);
i++;
for(int j=i;j<=n;j++)
{
if(a[j]>s.top())
{
flag=false;//not the "biggest"
//cout<<j<<' ';
printf("%d ",j);
break;
}
}
if(flag) printf("0 ");//cout<<0<<' ';
}*/
for(int i=n;i>=1;i--)
{
while(!s.empty()&&a[s.top()]<=a[i]) s.pop();
f[i]=s.empty()?0:s.top();
s.push(i);
}
for(int i=1;i<=n;i++) printf("%d ",f[i]);
return 0;
}