直方图dp。(我自己起的名字)
可以想到最后答案的形状一定可以是某个完整的柱形以自己完整的高度往左右扩散而得到的,扩散到什么地步呢?往左扩散到第一个小于自己高度的地方(从当前往左数),往右也扩散到第一个小于自己高度的地方(从当前往右数)。也就是说,中间这个完整的柱形一定是整个矩形的最低点(木板效应)。
(最后答案可能只包括自己柱形的一部分(比如11211),但在最后答案中一定可以找到某一个柱形被完整包含)
所以这N个柱形,每一个柱形都对应一个这样的基于自身的最大矩形。那么最终答案一定在这N个最大矩形之中。
那么问题就是怎么求每个柱形的最大矩形,其实只要从这个柱形的下标出发,分别往左右找第一个小于它高度的柱形,再记录下标就ok了。往左往右是同样的原理,我们只用分析一种即可。
- 那么怎么找往左的第一个小于自己的下标呢?(实际是记录这个下标的后一个,作为
l[i]),最简单的方法是从当前下标开始往左依次循环判断。 - 但是我们可以利用
l[i-1]来加速这个过程。前一个柱形h[i-1]和当前柱形h[i]的关系只有两种,如果h[i-1]<h[i],那么直接就判断出来了。 - 如果
h[i-1]>=h[i],那么我们可以跳过从i-1到l[i-1]这些柱形,直接去判断h[l[i-1]-1]与h[i]的关系。
以上加速过程建立在先前的值都已计算过的基础上,所以第一层循环从左往右,第二层循环从当前往左(这个东西是跳着找的,一段一段地跨越,充分利用历史信息)。
最后,这个题的高度上限给的很大(1e9),和底边乘起来会溢出的,需要转为__int64类型,输出用%I64d。查了一下才发现C++的64位整数的定义很乱,反正记住这三对应该可以应付各种环境了。
(1.long long,%lld 2.long long,%I64d 3.__int64,%I64d,参考资料)
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
#include <string>
#include <queue>
using namespace std; // 直方图dp
const int MAXN = 1e5;
int N;
int h[MAXN];
int l[MAXN], r[MAXN];
void init(){}
int main()
{
int t;
for (; ~scanf("%d", &N);)
{
if (N == 0) break;
init();
for (int i = 0; i < N; i++)
scanf("%d", &h[i]);
l[0] = 0;
for (int i = 1; i < N; i++)
{
for (t = i; t - 1 >= 0 && h[t - 1] >= h[i]; t = l[t - 1]);
l[i] = t;
}
r[N - 1] = N - 1;
for (int i = N - 2; i >= 0; i--)
{
for (t = i; t + 1 < N && h[t + 1] >= h[i]; t = r[t + 1]);
r[i] = t;
}
__int64 ans = -1;
for (int i = 0; i < N; i++)
ans = max(ans, ((__int64)h[i] * (__int64)(r[i] - l[i] + 1))); // 哈哈哈 64的类型转换不能等他俩乘完了再转,乘完都溢出了
printf("%I64d\n", ans);
}
return 0;
}
本文深入探讨了一种名为直方图DP的算法,通过分析柱形图中柱子的高度,寻找能形成最大矩形的策略。文章详细解释了如何从每个柱形出发,向左右寻找第一个低于自身高度的柱形,从而确定最大矩形的边界。同时,讨论了如何利用已计算的数据加速这一过程,避免重复计算,并提供了C++实现代码。
655

被折叠的 条评论
为什么被折叠?



