这个题是可以用动态规划,或者是单调栈、其实实质是差不多的;
这里讲一下单调栈,参考博客http://blog.csdn.net/dgq8211/article/details/7740610;
1. 什么是单调栈
单调栈就是保持了单调性和栈的性质;
单调递增的栈就是从栈尾到栈顶是单调递增的;
2. 单调栈能够解决的问题
1) 以自己为最小或最大值找到最大的区间,(对应 单调递增/单调递减);
2) 给定一个区间,找到这个区间的最大或最小值;
3. 单调栈的性质
1) 对于第一个出栈元素,它的右宽一定为0;
2) 对于第二个出栈元素,它的右宽为前一个出栈元素的总宽;
3) 对于第三个出栈元素,它的右宽为第二个出栈元素的总宽;
…………
4) 好了,终于遇到了比自己小的元素可以入栈了,那么入栈元素的左宽为上次出栈元素的总宽+1(自身);(若无出栈元素,则左宽为1(自身)
5) 最后将栈中所有元素出栈考虑所有情况;
简言之,出栈元素的右宽为上个出栈元素的总宽,入栈元素的左宽为上个出栈元素的总宽+1;
单调递增 左宽就是比向左数的x个数小,右宽就是比向右数的y个数都小;
单调递减 左宽就是比向左数的x个数大,右宽就是比向右数的y个数都大;
4. 以下面数据模拟一下单调递减栈的操作
1入栈,无出栈元素,左宽为1;
5入栈,无出栈元素,左宽为1;
5出栈,第一个出栈右宽为0;
4入栈,左宽为上个出栈元素的总宽+1=2;
8入栈,无出栈元素,左宽为1;
8出栈,第一个出栈,右宽为0;
6入栈,左宽为上个出栈的总宽+1;
7入栈,无出栈元素,左宽为1;
7出栈,第一个出栈,右宽为0;
6出栈,第二个出栈,右宽为上个元素的总宽:1;
4出栈,第三个出栈,右宽为上个元素的总宽:3;
3入栈,左宽为上个出栈的总宽+1:6;
3出栈,第一个出栈右宽为0;
2入栈,左宽为上一个出栈的总宽+1:7;
//全部出栈:
2出栈,第一个出栈,右宽为0;
1出栈,第二个出栈的,右宽为上个出栈的总宽:7;
5. 单调栈的实现
hdu1506(单调递减栈)poj2339,hrbustoj 2326(需long long)
单调递减栈:如果是以各个元素为最大值找到最大区间的话 q[0]=inf, h=inf-1;
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int maxn=1e3+10;
#define N 100005
int q[N]={-1},w[N];//w记录左宽;
int main()
{
int n,h;
while(scanf("%d",&n)&&n)
{
int top=0;
ll ans=0;
for(int i=1;i<=n+1;i++)
{
if(i!=n+1)
scanf("%d",&h);
else
h=0;
if(h>q[top])
q[++top]=h,w[top]=1;
else
{
ll cnt=0; //第一个出栈的右宽为0;
while(h<=q[top])
{
ans=max(ans,(w[top]+cnt)*q[top]); //(左宽+右宽)*高度;
cnt=cnt+w[top--]; //第(i>1)出栈的右宽为上一个的总宽;
}
// 终于找到比自己小的数字了,可以入栈了,入栈会得到左宽,左宽为上一个出栈元素的总宽+1;
q[++top]=h;
w[top]=cnt+1;
}
}
printf("%I64d\n",ans);
}
return 0;
}
若是还想记录每个元素作为最小值的区间呢?
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int maxn=1e3+10;
#define N 100005
int q[N]={-1},w[N];//w记录左宽;
int a[N],L[N],R[N];//记录指定元素左宽右宽,输出为左右区间;
int main()
{
int n,h;
stack st;
while(scanf("%d",&n)&&n)
{
while(!st.empty()) st.pop();
L[1]=1,R[n]=0; //记录的是左宽右宽;
int top=0;
ll ans=0;
for(int i=1;i<=n+1;i++)
{
if(i!=n+1)
scanf("%d",&h);
else
h=0;
if(h>q[top])
{
//无出栈元素,左宽为1;
q[++top]=h,w[top]=1;
L[i]=1;/****/
}
else
{
ll cnt=0; //第一个出栈的右宽为0;
while(h<=q[top])
{
int id=st.top();st.pop();
R[id]=cnt;/***/
ans=max(ans,(w[top]+cnt)*q[top]); //(左宽+右宽)*高度;
cnt=cnt+w[top--]; //第(i>1)出栈的右宽为上一个的总宽;
}
// 终于找到比自己小的数字了,可以入栈了,入栈会得到左宽,左宽为上一个出栈元素的总宽+1;
q[++top]=h;
w[top]=cnt+1;
L[i]=w[top];/***/
}
st.push(i);
}
printf("%I64d\n",ans); //输出最大面积;
for(int i=1;i<=n;i++)
{
printf("%d:%d %d\n",i,i-L[i]+1,i+R[i]); //输出以a[i]为最小值的区间;
}
}
return 0;
}
答案:保存各位的左宽右宽:用stack维护下标即可~