Time Limit: 6000MS | Memory Limit: 65536K | |
Total Submissions: 7894 | Accepted: 1960 |
Description
Input
Output
Sample Input
4
5 4 3 6
4
6 5 4 3
Sample Output
1
-1
分析:本题题意为,
有很多组木棍,每组读入一个n,为该组的木棍数,
定义完美区间是区间内的所有木棍都比左端点长,比右端点短,
求最长的完美区间。
看到这道题,首先是枚举的n^2算法,
由于数据范围大,
我又想到单调队列与rmq的组合解法,由于我不会rmq的st算法,
所以,我选择了另一种方法,
纯单调栈来解题。
具体点的,两方向分别单调栈操作,
正向,找到不大于该数的第一个数,记录位置,
反向,找到不小于该数的第一个数,记录位置,
再正向扫一遍,
对每个点,我们检查该点到该点右边界间的点,
看是否存在一个点的左边界小于等于原来点的位置,
若存在,则这两点之间的区间是满足题意的区间,
判断是不是最长的完美区间,并更新值。
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.