题目链接

题意

对于一个长度为\(n\)的非负整数数列\(a_1,a_2,…,a_n\),求\(max_{1≤l≤r≤n}f(l,r)\), 其中

\[f(l,r)=min(a_l,a_{l+1},…,a_r)×(a_l+a_{l+1}+⋯+a_r)
\]

思路

显然,最小值必为数列中的某个数,所以题目转化为:

对于数列中的 每个数,找 使其 为区间最小值的 最大的区间,即该点向左向右最远能延伸到的地方

// 是不是和那道找最大矩形面积如出一辙?

法一:dp

用\(l[\ ]\)和\(r[\ ]\)记录当前位置向左向右延伸的最远距离。

从左向右计算\(l[\ ]\),计算当前位置的\(l[\ ]\)值时沿着之前计算并记录下来的信息跳着向前找。

\(r[\ ]\)值同理。

法二:单调栈

只需一次遍历,维护一个单调增的栈。

当前栈中每一个元素的右端点至少都是当前位置,左端点则都是之前已记录位置。

踢掉元素进行更新的时候要注意将踢掉元素的左端点的值继承下来,因为踢掉它们意味着它们比当前元素要大,所以当前元素的左端点必然能延伸到它们能延伸到的位置。

由上述过程可看出,当每个元素被踢出来时,其左端点值和右端点值都最终确定了,因而可以计算以它为最小值的这一段对应的答案。

此外,还要注意在最后补上一个最小元素(-1),这是为了保证所有的元素最终都能被踢出来。

  1. 说的可能有点抽象...拿样例来说吧
  2. 3 1 6 4 5 2
  3. // 以下符号中圆括号中的值代表左端点,方括号中的pair代表左端点和右端点
  4. // 注意:为了看起来直观,下例中栈中的元素均以其对应的值替代,实际操作中真正记录的是下标
  5. *step 1.*
  6. 3进栈
  7. 3(1)
  8. *step 2.*
  9. 踢掉3[1,1],1进栈
  10. 1(1)
  11. *step 3.*
  12. 6进栈
  13. 1(1), 6(3)
  14. *step 4.*
  15. 踢掉6[3,3],4进栈
  16. 1(1), 4(3)
  17. *step 5.*
  18. 5进栈
  19. 1(1), 4(3), 5(5)
  20. *step 6.*
  21. 踢掉5[5,5],踢掉4[3,5],2进栈
  22. 1(1), 2(6)
  23. *step 7.*
  24. 踢掉2[6,6],踢掉1[1,6],-1进栈
  25. -1(7)

Code

法一

  1. #include <stdio.h>
  2. #define maxn 100010
  3. using namespace std;
  4. typedef long long LL;
  5. int a[maxn], l[maxn], r[maxn];
  6. LL pre[maxn];
  7. int main() {
  8. int n;
  9. scanf("%d", &n);
  10. for (int i = 1; i <= n; ++i) scanf("%d", &a[i]), pre[i] = pre[i-1] + a[i];
  11. l[1] = 0;
  12. for (int i = 2; i <= n; ++i) {
  13. int p = i-1;
  14. while (p && a[p] >= a[i]) p = l[p];
  15. l[i] = p;
  16. }
  17. r[n] = n+1;
  18. for (int i = n-1; i > 0; --i) {
  19. int p = i+1;
  20. while (p != n+1 && a[p] >= a[i]) p = r[p];
  21. r[i] = p;
  22. }
  23. LL ans = -1;
  24. int ll, rr;
  25. for (int i = 1; i <= n; ++i) {
  26. LL temp = (pre[r[i]-1] - pre[l[i]]) * a[i];
  27. if (temp > ans) ans = temp, ll = l[i]+1, rr = r[i]-1;
  28. }
  29. printf("%lld\n%d %d\n", ans, ll, rr);
  30. return 0;
  31. }

法二

  1. #include <stdio.h>
  2. #include <iostream>
  3. #define maxn 100010
  4. using namespace std;
  5. typedef long long LL;
  6. int a[maxn], st[maxn], l[maxn];
  7. LL pre[maxn];
  8. int main() {
  9. int n;
  10. scanf("%d", &n);
  11. for (int i = 1; i <= n; ++i) {
  12. scanf("%d", &a[i]);
  13. pre[i] = pre[i-1] + a[i];
  14. }
  15. int top = 0;
  16. a[++n] = -1;
  17. LL ans = -1; int lll, rrr;
  18. for (int i = 1; i <= n; ++i) {
  19. int x, ll=i, rr=i-1;
  20. while (top && a[i] < a[st[top-1]]) {
  21. x = st[--top], ll = l[x];
  22. LL temp = a[x] * (pre[rr] - pre[ll-1]);
  23. if (temp > ans) ans = temp, lll = ll, rrr = rr;
  24. }
  25. l[i] = ll; st[top++] = i;
  26. }
  27. printf("%lld\n%d %d\n", ans, lll, rrr);
  28. return 0;
  29. }

后话

既然上面都提到了最大矩形面积...写博客的时候就一时兴起回头翻了翻

hdu 1505 1506 2870 dp小礼包

原来那个时候我就学过一遍单调栈的做法啊...(叹

不管不管现在的代码至少比以前写得好看(

poj 2796 Feel Good dp || 单调栈的更多相关文章

  1. POJ 2796 Feel Good 【单调栈】

    传送门:http://poj.org/problem?id=2796 题意:给你一串数字,需要你求出(某个子区间乘以这段区间中的最小值)所得到的最大值 例子: 6 3 1 6 4 5 2 当L=3,R ...

  2. POJ 2796 Feel Good(单调栈)

    传送门 Description Bill is developing a new mathematical theory for human emotions. His recent investig ...

  3. Feel Good POJ - 2796 (前缀和+单调栈)(详解)

    Bill is developing a new mathematical theory for human emotions. His recent investigations are dedic ...

  4. 51nod 1215 数组的宽度&poj 2796 Feel Good(单调栈)

    单调栈求每个数在哪些区间是最值的经典操作. 把数一个一个丢进单调栈,弹出的时候[st[top-1]+1,i-1]这段区间就是弹出的数为最值的区间. poj2796 弹出的时候更新答案即可 #inclu ...

  5. POJ 2796:Feel Good 单调栈经典题

    Feel Good Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 11626   Accepted: 3212 Case T ...

  6. POJ 2796:Feel Good 单调栈

    题目,给定一个数列,n <= 1e5 .要求找出一个区间,使得其内区间最小值 * 区间总和的值最大,要求输出区间. 首先先维护一个单调递增的栈,同时记录一个lef值表示:lef[i]表示当前栈内 ...

  7. poj 2769 感觉♂良好 (单调栈)

    poj 2769 感觉♂良好 (单调栈) 比尔正在研发一种关于人类情感的新数学理论.他最近致力于研究一个日子的好坏,如何影响人们对某个时期的回忆. 比尔为人的一天赋予了一个正整数值. 比尔称这个值为当 ...

  8. [luogu]P1169 [ZJOI2007]棋盘制作[DP][单调栈]

    [luogu]P1169 [ZJOI]棋盘制作 ——!x^n+y^n=z^n 题目描述 国际象棋是世界上最古老的博弈游戏之一,和中国的围棋.象棋以及日本的将棋同享盛名.据说国际象棋起源于易经的思想,棋 ...

  9. poj 3250 Bad Hair Day (单调栈)

    http://poj.org/problem?id=3250 Bad Hair Day Time Limit: 2000MS   Memory Limit: 65536K Total Submissi ...

随机推荐

  1. 4.在Cisco Packet Tracerl里路由器密码重置

    在路由器的特权模式的密码忘记的情况下,关闭路由器的电源,在接通电源,在路由器载入的时候,按ctrl+c,直接进入monitor模式 输入:confreg 0x2142 reset 重新进入后 enab ...

  2. bootstrap-图片样式记录

    //三种形状<img src=”img/pic.png” alt=”图片” class=”img-rounded” /><img src=”img/pic.png” alt=”图片” ...

  3. 牛客练习赛42 A 字符串

    题目描述 给定两个等长的由小写字母构成的串 A,BA,B,其中 |A|=|B|=n|A|=|B|=n. 现在你需要求出一个子区间 [l,r][l,r] 使得 LCP(A[l,r],B[l,r])×LC ...

  4. POJ 3370 Halloween treats(抽屉原理)

    Halloween treats Every year there is the same problem at Halloween: Each neighbour is only willing t ...

  5. SOA:面向服务编程——竹子整理

    .net中如webservice,wcf,webapi,均可作为服务层,单独部署,而界面UI则部署在另一台服务器上,所有的业务逻辑均在服务层的业务层中进行. 这样一来,我们的UI其实就可以不限制语言, ...

  6. GSMM数据库设计小结

    边写边结 1.新增,删除,修改在各自的DAL中进行,查,可以新建一个DAL,里面是需要的各个属性,跨表,不同表属性整合成一个对象(集合)返回,输出到用户界面.

  7. 怎么把myeclipse项目导入IDEA中

    先把myeclipse下的项目拷贝到IDEA的部署目录中,把一些不用的配置文件删除,只留下一个干净的项目 打开IDEA,点击import Project,引入一个项目,选择IDEA部署目录下刚拷贝过去 ...

  8. mysql进阶三四五六

    排序查询 一.语法 select 查询表 from 表 where 筛选条件 order by 排序列表[asc / desc] 特点: 1.asc:升序 desc:降序 2.排序列表之中支持单字段, ...

  9. MySQL安装教程&Navicat安装

    一.下载MySQL http://jingyan.baidu.com/article/e3c78d64412ae83c4c85f5fd.html 首先打开MySQL官网,找到Downloads标签,点 ...

  10. 创建OpenStack的存储云

    OPENSTACK内部 OpenStack是一个开源的云平台项目,是由NASA发起,Rackspace在2010作为一个项目进行主导.源代码是由OpenStack基金会管理并在准许Apache许可下发 ...