题目描述
数轴上有 n (1<=n<=25000)个闭区间 [ai, bi],选择尽量少的区间覆盖一条指定线段 [1, t] (1<=t<=1,000,000)。覆盖整点,即(1,2)+(3,4)可以覆盖(1,4)。不可能办到输出-1。
输入
输入包含多组数据。每组数据用一个整数n来表示直方图中小矩形的个数,你可以假定1 <= n <= 100000. 然后接下来n个整数h1, …, hn, 满足 0 <= hi <= 1000000000. 这些数字表示直方图中从左到右每个小矩形的高度,每个小矩形的宽度为1。 测试数据以0结尾。
输出
对于每组测试数据输出一行一个整数表示答案。
样例输入
7 2 1 4 5 1 3 3
4 1000 1000 1000 1000
0
样例输出
8
4000
思路
综述
这道题主要考察了一个新的结构:单调栈(需满足以下条件
1)满足普通栈的操作
2)栈中的元素有序
对于这道题,可以对于每一个矩形都找到该矩形左右两边第一个比他高度小的矩形的编号。如果不存在则声明为边界即0和n
主要过程:
利用的单调栈是从栈底到栈顶是递增
1)从左到右搜一遍,找到每个矩形的右侧的边界;
2)从右到左搜一遍,找到每个矩形的左侧的边界;
过程
结构体:
开大数组来存储每一个矩形。
struct node {
long long value;
long long l;//存左侧编号
long long r;//存右侧编号
long long number;//编号
};
node Node[100010];
Step1:输入
采用STL中的栈
其中记录一下,每个点的number值,不然当栈pop的时候,会丢失形参。
因为S中存储的每个值和上面开的大数组中的值在内存中非同一块,所以这里记录一下number值,以便之后修改和记录。
cin >> n;
if (n == 0)break;
stack<node> S;
for (long long i = 0; i < n; i++) {
cin >> Node[i].value;
Node[i].number = i;
}
Step2:确定右端点
第一步:如果栈空,直接push
if (S.empty()) {
S.push(Node[i]);
}
第二步:
如果栈非空,则将栈内每一个比当前元素值小的值都pop
pop出来的同时,对出栈的矩形记录右端点的值
while (!S.empty() && S.top().value > Node[i].value) {
Node[S.top().number].r = i;
S.pop();
}
S.push(Node[i]);
第三步:
遍历完一遍之后,对栈内所有的元素,其右端点都是n
while (!S.empty()) {
Node[S.top().number].r = n;
S.pop();
}
Step3:确定左端点
与确定右端点类似,只不过方向相反
//确定左端点
for (long long i = n - 1; i >= 0; i--) {
if (S.empty()) {
S.push(Node[i]);
}
else {
while (!S.empty() && S.top().value > Node[i].value) {
Node[S.top().number].l = i;
S.pop();
}
S.push(Node[i]);
}
}
while (!S.empty()) {
Node[S.top().number].l = -1;
S.pop();
}
总结
1、本题中0 <= hi <= 1000000000,如果用int可能会精度不够
int的范围是-2147483648~2147483647
2、STL中开栈。栈中存的变量和全局变量大数组中的非同一个,仅仅是值相等,所以出栈的元素再也找不回了。
这里结构体中记录了number,所以操作栈的同时也操作了大数组,虽然复杂度高了些,但能够保证出栈的元素找回来。
代码
#include <iostream>
#include <stack>
using namespace std;
struct node {
long long value;
long long l;
long long r;
long long number;
};
node Node[100010];
int main() {
long long n;
while (1) {
cin >> n;
if (n == 0)break;
stack<node> S;
for (long long i = 0; i < n; i++) {
cin >> Node[i].value;
Node[i].number = i;
}
//确定右端点
for (long long i = 0; i < n; i++) {
//如果栈空,直接push
if (S.empty()) {
S.push(Node[i]);
}
else {
//如果栈非空,则将栈内每一个比当前元素值小的值都pop
while (!S.empty() && S.top().value > Node[i].value) {
//pop出来的同时,对出栈的矩形记录右端点的值
Node[S.top().number].r = i;
S.pop();
}
S.push(Node[i]);
}
}
//遍历完一遍之后,对栈内所有的元素,其右端点都是n
while (!S.empty()) {
Node[S.top().number].r = n;
S.pop();
}
//确定左端点
//与确定右端点类似,只不过方向相反
for (long long i = n - 1; i >= 0; i--) {
if (S.empty()) {
S.push(Node[i]);
}
else {
while (!S.empty() && S.top().value > Node[i].value) {
Node[S.top().number].l = i;
S.pop();
}
S.push(Node[i]);
}
}
while (!S.empty()) {
Node[S.top().number].l = -1;
S.pop();
}
long long tot = -10000;
for (long long i = 0; i < n; i++) {
if (Node[i].value * (Node[i].r - Node[i].l - 1) > tot)
tot = Node[i].value * (Node[i].r - Node[i].l - 1);
}
cout << tot << endl;
}
}