HDU 1506 Largest Rectangle in a Histogram

传送门

直方图dp。(我自己起的名字)
可以想到最后答案的形状一定可以是某个完整的柱形以自己完整的高度往左右扩散而得到的,扩散到什么地步呢?往左扩散到第一个小于自己高度的地方(从当前往左数),往右也扩散到第一个小于自己高度的地方(从当前往右数)。也就是说,中间这个完整的柱形一定是整个矩形的最低点(木板效应)。
(最后答案可能只包括自己柱形的一部分(比如11211),但在最后答案中一定可以找到某一个柱形被完整包含)

所以这N个柱形,每一个柱形都对应一个这样的基于自身的最大矩形。那么最终答案一定在这N个最大矩形之中。

那么问题就是怎么求每个柱形的最大矩形,其实只要从这个柱形的下标出发,分别往左右找第一个小于它高度的柱形,再记录下标就ok了。往左往右是同样的原理,我们只用分析一种即可。

  1. 那么怎么找往左的第一个小于自己的下标呢?(实际是记录这个下标的后一个,作为l[i]),最简单的方法是从当前下标开始往左依次循环判断。
  2. 但是我们可以利用l[i-1]来加速这个过程。前一个柱形h[i-1]和当前柱形h[i]的关系只有两种,如果h[i-1]<h[i],那么直接就判断出来了。
  3. 如果h[i-1]>=h[i],那么我们可以跳过从i-1l[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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值