先看例题:
Loongint的花篮
【Description】
Loongint要和MM结婚了。在两人的走进礼堂的红地毯两侧,需要摆一些装饰用的花篮,有一些不同高度的花篮,现在这些花篮被Loongint依照自己的美学观念编号为S1,S2,S3…Sn(两侧的花篮高度一样)。可Loongint的MM对这些花篮的摆放方式有不同的看法,她觉得满足以下条件的花篮摆放才是最好的。
如果对于区间[Si,Sj](1<=i<j<=n)中任意的花篮都比Si高且比Sj低,那么这个区间称为一个美学区间。对于所有的美学区间,其长度(定义为j-i)都必须小于等于k,如果有长度大于k的美学区间,MM就会不高兴,Loongint就会有麻烦…
【Input】
第一行为m。表示有m组测试数据。
对于每一组:
第一行n,k,分别表示花篮的数量和美学区间的最大长度。
第二行为n个数,分别表示S1,S2,S3…Sn的值。
【Output】
如果根本不存在美学区间,输出-1。
如果存在美学区间,那么如果任意区间的长度都小于等于k,那么输出最大的长度,否则输出最大长度比k大多少(MaxLength-k)。
【Sample Input】
3
4 2
5 4 3 6
4 1
6 5 4 3
4 2
1 2 3 4
【Sample Output】
1
-1
1
【Hint】
对于30%的测试数据,1<=n<=100。
对于60%的测试数据,1=<n<=5555。
对于100%的测试数据,1<=n<=100000,0<Si<=100000,1=<m<=3。
分析
本题的意思是找到一个区间,让区间的头元素是最小的尾元素是最小的(中间不能有和首尾相同的元素),即区间的最值是头和尾,所以很自然可以想到求区间最值的ST算法,但这样操作有两个弊端:
1. 必须枚举区间长度,可能超时
2. 难以判断区间里是否有相同元素
所以ST算法难以胜任,所以我们采用单调栈,顾名思义就是在入栈时遵循单调原则,可以求出一个元素向左(或向右)所能扩展到的最大长度,并不是说在这一段区间内是单调的,而是保证在该区间内该元素一定是最大或最小。因此,对于这道题我们可以求两次单调栈,以确定两端点一定满足题目条件。
不能一直维持栈,使其一开始下降就全部弹出栈,因为在要求的区间内不一定非得是单调
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 program sf;
2 var m,n,k,i,j,top,ans,kk:longint;
3 a,s:array[0..100001] of longint;//s记录入栈元素的标号
4 l,r:array[0..100001] of longint;
5 begin
6 assign(input,'baskets.in'); reset(input);
7 //assign(output,'baskets.out'); rewrite(output);
8 readln(m);
9 for kk:=1 to m do
10 begin
11 readln(n,k);
12 for i:=1 to n do
13 begin
14 read(a[i]);
15 r[i]:=i;//初始化
16 l[i]:=i;
17 end;
18 a[n+1]:=-maxlongint;//右边界赋值为最小值,使得元素能够全部出栈
19 top:=1;
20 s[top]:=1;
21 for i:=2 to n+1 do
22 begin
23 while (a[s[top]]>=a[i]) and (top>0) do
24 begin
25 r[s[top]]:=i-1;//向左扩展到第i个时不能满足单调栈的性质,所以s[top]所能达到的最远为i-1
26 dec(top);
27 end;
28 inc(top);
29 s[top]:=i;
30 end;
31 a[0]:=maxlongint;//为了使元素全部出栈
32 top:=1;
33 s[top]:=n;
34 for i:=n-1 downto 0 do
35 begin
36 while (a[s[top]]<=a[i]) and (top>0) do
37 begin
38 l[s[top]]:=i+1;//同上
39 dec(top);
40 end;
41 inc(top);
42 s[top]:=i;
43 end;
44 ans:=0;
45 for i:=1 to n-1 do
46 if r[i]<>i then
47 begin
48 if r[i]-l[i]<=ans then continue;//如果,i所能达到的最右-最左仍然比ans小,跳出(符合题意的区间一定是[l[i],r[i]]的子集)
49 for j:=r[i] downto i+1 do
50 begin
51 if j-i<=ans then continue;//道理与上类似
52 if l[j]<=i then//如果j所能到达的最左端小于i,那么i一定是[i,j]中的最小值,j是最大值(见图)
53 if j-i>ans then
54 begin
55 ans:=j-i;
56 break;
57 end;
58 end;
59 end;
60 if ans=0 then writeln(-1);
61 if (ans<=k) and (ans<>0) then writeln(ans);
62 if ans>k then writeln(ans-k);
63 end;
64 close(input);
65 //close(output);
66 end.
本题双方向单调栈解法:http://www.cnblogs.com/dizzy/archive/2011/10/25/2224089.html