poj2452 Sticks Problem——双方向单调栈
Sticks Problem
Time Limit: 6000MS | Memory Limit: 65536K | |
Total Submissions: 7894 | Accepted: 1960 |
Description
Xuanxuan has n sticks of different length. One day, she puts all her sticks in a line, represented by S1, S2, S3, ...Sn. After measuring the length of each stick Sk (1 <= k <= n), she finds that for some sticks Si and Sj (1<= i < j <= n), each stick placed between Si and Sj is longer than Si but shorter than Sj.
Now given the length of S1, S2, S3, …Sn, you are required to find the maximum value j - i.
Now given the length of S1, S2, S3, …Sn, you are required to find the maximum value j - i.
Input
The input contains multiple test cases. Each case contains two lines.
Line 1: a single integer n (n <= 50000), indicating the number of sticks.
Line 2: n different positive integers (not larger than 100000), indicating the length of each stick in order.
Line 1: a single integer n (n <= 50000), indicating the number of sticks.
Line 2: n different positive integers (not larger than 100000), indicating the length of each stick in order.
Output
Output the maximum value j - i in a single line. If there is no such i and j, just output -1.
Sample Input
4
5 4 3 6
4
6 5 4 3
Sample Output
1
-1
分析:本题题意为,
有很多组木棍,每组读入一个n,为该组的木棍数,
定义完美区间是区间内的所有木棍都比左端点长,比右端点短,
求最长的完美区间。
看到这道题,首先是枚举的n^2算法,
由于数据范围大,
我又想到单调队列与rmq的组合解法,由于我不会rmq的st算法,
所以,我选择了另一种方法,
纯单调栈来解题。
具体点的,两方向分别单调栈操作,
正向,找到不大于该数的第一个数,记录位置,
反向,找到不小于该数的第一个数,记录位置,
再正向扫一遍,
对每个点,我们检查该点到该点右边界间的点,
看是否存在一个点的左边界小于等于原来点的位置,
若存在,则这两点之间的区间是满足题意的区间,
判断是不是最长的完美区间,并更新值。
View Code
1 program poj_2452;
2 var
3 i,j,n,m,k,t,b:longint;
4 s,w,a,l,r:array[0..50001]of longint;
5 begin
6 assign(input,'poj.in');
7 reset(input);
8 while not eof do//对每组数据处理
9 begin
10 fillchar(l,sizeof(l),0);
11 fillchar(r,sizeof(r),0);
12 fillchar(w,sizeof(w),0);
13 fillchar(a,sizeof(a),0);
14 fillchar(s,sizeof(s),0);
15 //这些fillchar本来为了保险才加的,对本题来说用处不大
16 readln(n);
17 a[0]:=maxlongint;
18 a[n+1]:=-maxlongint;//人工设立边界
19 if n=0 then break;//防止出现数据有空行现象
20 m:=0;
21 for i:=1 to n do read(a[i]);//读入
22 readln;
23 //s即stack,栈,保证其单调性质
24 //l,r分别记录木棍扩展出去的左右边界
25 {
26 我这里的表示方法是这样的,
27 对于任意的木棍x
28 l[x]<x且l[x-1]>=x
29 r[x]>x且r[x+1]<=x
30 即
31 l[x]~x-1的木棍都比x短
32 x+1~r[x]的木棍都比x长
33 }
34 t:=1;s[1]:=a[0];w[1]:=0;
35 //正向单调栈操作,初始化,栈中置最大值
36 //(因为,有些木棍左边的都比它短,那么l[x]中不会有值)
37 //w存的是栈中对应木棍在原序列的位置
38 for i:=1 to n+1 do
39 begin
40 k:=a[i];
41 while (t>0)and(k<=s[t]) do
42 begin
43 r[w[t]]:=i-1;//为栈顶元素的右边界赋值
44 dec(t);//弹栈操作
45 end;
46 inc(t);
47 s[t]:=k;w[t]:=i;//把新的元素入栈
48 end;
49 //以下为逆向单调栈
50 t:=1;s[1]:=a[n+1];w[1]:=n+1;
51 for i:=n downto 0 do
52 begin
53 k:=a[i];
54 while (t>0)and(k>=s[t]) do
55 begin
56 l[w[t]]:=i+1;
57 dec(t);
58 end;
59 inc(t);
60 s[t]:=k;w[t]:=i;
61 end;
62 {
63 以下为扫描操作
64 寻找满足要求的区间,
65 并更新最大值m
66 }
67 for i:=1 to n do
68 for j:=m+i+1 to r[i] do//这重循环要注意,
69 //原来我是这样打的
70 //for j:=i+1 to r[i] do
71 //但严重超时(6000MS+)
72 //用现在的这种方法减少了循环量,即可AC
73 //耗时700MS+
74 if l[j]<=i then
75 if j-i>m then m:=j-i;//更新操作
76 if m=0 then writeln(-1)
77 else writeln(m);//输出
78 end;
79 close(input);
80 end.