題目描述
數軸上有 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;
}
}