pku2796_Feel Good

http://poj.org/problem?id=2796

这道题跟2559还有3494都是求最大子矩阵。

对于每个a[i],求以其为最小值的最大连续的区间,即是以其左边最后一个比它大的数的位置为左边界,以其右边最后一个比它大的数的位置为右边界,例(a[]从1位置开始存放):
      1 6 7 4 5 3 2
      a[4] = 4的左边界是2,右边界是5,a[4]的区间是2~5

然后计算出每个数在各自的区间内的值(总和 * a[i],因为a[i]是这个区间的最小值),枚举出的最大值即为所求。

(算法参见2003国家集训队论文“王知昆:《浅谈用极大化思想解决最大子矩形问题》”算法2,O(nm)的算法。)

注意:结果要用long long保存!

代码用到了单调栈扫描。。。嗯嗯,好东西~~


01 #include <cstdio>
02 using namespace std;
03
04 const int N = 100010;
05 int stack [N ], a [N ], l [N ], r [N ];
06 long long sum [N ];  //sum[i]是前缀和,即前i个数的总和
07
08 int main()
09 {
10     int n;
11     scanf( "%d" , &n);
12     sum [ 0 ] = 0;
13     for ( int i = 1; i <= n; i ++)
14     {
15         scanf( "%d" , & a [ i ]);
16         sum [ i ] = sum [ i - 1 ] + a [ i ];
17     }
18    
19     //求出每个数的左边界
20     int top = 0;
21     stack [ 0 ] = 0;
22     a [ 0 ] = - 1;
23     for ( int i = 1; i <= n; i ++)
24     {
25         while ( a [ stack [ top ]] >= a [ i ])
26             top --;
27         l [ i ] = stack [ top ] + 1;
28         stack [ ++ top ] = i;
29     }
30    
31     //求出每个数的右边界
32     top = 0;
33     stack [ 0 ] = n + 1;
34     a [n + 1 ] = - 1;
35     for ( int i = n; i >= 1; i --)
36     {
37         while ( a [ stack [ top ]] >= a [ i ])
38             top --;
39         r [ i ] = stack [ top ] - 1;
40         stack [ ++ top ] = i;
41     }
42    
43     long long ans = - 1;
44     int id = 0;
45     for ( int i = 1; i <= n; i ++)
46     {
47         long long temp = ( sum [ r [ i ]] - sum [ l [ i ] - 1 ]) * a [ i ];
48         if ( temp > ans)
49         {
50             ans = temp;
51             id = i;
52         }
53     }
54     printf( "%lld \n %d %d \n " , ans , l [ id ], r [ id ]);
55 }

你可能感兴趣的:(pku2796_Feel Good)