pku2796_Feel Good

Posted on 2010-12-07 14:41 小小菜鸟蛋 阅读(336) 评论(0)  编辑 收藏 引用 所属分类: 某蛋的解题报告

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 }

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理