CSP_training Week5 ProblemA(HDU-1506)單調棧問題

Week5 Problem A 單調棧求解

數據結構概述

衆做周知棧結構是一種很常用的數據結構,在c++的STL庫中也有封裝好的棧結構stack,可以使用下面的語句進行調用

#include<stack>

單調棧如其名,一種單調的棧結構,可以通過維持一個自頂向下遞增(遞減)數據序列,來完成目的數據的訪問控制和大小判斷。一般來說,單調棧主要用來解決找到某個點的全局最大&最小範圍,從而進行下一步問題的求解。例題請看下文:

題目概述

題目敘述

給一個直方圖,求直方圖中的最大矩形的面積。例如,下面這個圖片中直方圖的高度從左到右分別是2, 1, 4, 5, 1, 3, 3, 他們的寬都是1,其中最大的矩形是陰影部分。
圖例

INPUT & 輸入樣例

輸入包含多組數據。每組數據用一個整數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

OUTPUT & 輸出樣例

對於每組測試數據輸出一行一個整數表示答案。
輸出樣例

8
4000

題目重述和坑點

題意比較明確,給定一個直方圖序列,求出其內部可容納的最大的矩形大小,但是坑可不淺,所以還需仔細分析題意。

解題思路

坑一:合法矩形塊的定義,瀏覽題目的示意圖可以看出,合法的最大矩形塊應該滿足該矩形塊左右邊界兩側的直方圖數值都小於他,也就是“矩形剛好被截斷”的位置。
分析清楚了上面的定義,我們可以思考,圖中某個x的合法最大矩形面積該怎麼求?
分析一下樣例結合基本數學公式可以得出如下:

S=y(x)×(x延伸上界-x延伸下界)

問題就被轉換成了求出每個x的延伸上界和延伸下界,總結下即求出以x作爲最小值的連續區間的上下界。轉換到這裏,我們就可以使用固定的方法:單調棧來求解問題。
前面提及:
單調棧可以用來求解全局範圍內的,x作爲最大值(最小值)的連續區間。利用單調棧的單調性質我們可以將問題分爲以下三種:(以自棧底向棧頂的遞增棧爲例)
1、棧空–直接壓棧
2、棧非空&要插入的元素大於等於棧頂元素–壓棧
3、棧非空&要插入的元素小於棧頂元素–彈出棧頂元素並再次判斷,直至滿足條件2並壓棧。在這一步重要的是另外一個操作,要將被彈出點作爲最小值的延伸上限記爲壓棧的元素位置。
按照上面的步驟,可以得到每個點右側的延伸上限,訪問順序倒置,可以得到每個點左側的訪問下限。剩下步驟暴力即可輕鬆求解。
看到這裏你一定認爲結束了,那麼你就會像傻傻的博主一樣:
愛題目中在這裏插入圖片描述
這又是爲什麼呢?
且看坑二:在題目中看到了點的取值大小最多是1e9,正常來說int是可以存下來的,但是不要忽略的是,我們計算的是面積,一單某個矩形的寬度接近1e9,長度接近1e5,就會觸發第二個坑點,int不足以存下來面積大小,後面的面積也會失效,導致不明就裏的wa掉。
修改方案:long long大法好,long long就完事了

總結

總的來說題目難度不大,主要考察對單調棧的模型是否熟悉,但是坑點確實容易觸發,以後在看到數據取值達到1e9就要注意會不會因爲運算超出範圍導致求解錯誤了!
單調棧在很多相似問題中都有應用,只要給定的關鍵字有下列幾個:
全局
以x爲最小值的最大區間
x左邊(右邊)第一個大於(小於)x的值
單調棧往往能成爲解決問題的好辦法!

題目源碼

#include<iostream>
#include<stdio.h>
#include<stack>
#include<algorithm>
using namespace std;
long long n;
long long a[100010];long long L[100010];long long R[100010];
stack<int> s;
//對於i來說
//L:i能向左邊延伸的最靠左的位置 
//L[n]  等於他左邊第一個比他小的位置-1
//R: i能向右邊延伸的最靠右的位置
//R[n]  等於他右邊第一個比他小的位置+1
/*模擬一個例子:
[3 1 2 4 0]
[3]
[3 1]
[1] R[1]=2-1
[1 2]
[1 2 4]
[1 2 4 0]
[1 2 0] R[4]=5-1=4
[1 0]  R[3]=5-1=4
[0 ] R[2]=5-1=4
R[5]=5
*/
void solve1()
{
    for(int i=1;i<=n;i++)
    {
    	if(s.empty())
    	s.push(i);
    	else
    	{
        	if(a[s.top()]<=a[i])
            s.push(i);
            else
            {
            	while(a[s.top()]>a[i])
        		{
            		R[s.top()]=i-1;
            		s.pop();
            		if(s.empty())
            		break;
        		}
        		s.push(i);
			}
    	}
    }
    while(!s.empty())
    {
        R[s.top()]=n;
        s.pop();
    }
}
void solve2()
{
    for(int i=n;i>=1;i--)
    {
    	if(s.empty())
    	s.push(i);
    	else
    	{
        	if(a[s.top()]<=a[i])
            s.push(i);
            else
            {
            	while(a[s.top()]>a[i])
        		{
            		L[s.top()]=i+1;
            		s.pop();
            		if(s.empty())
            		break;
        		}
        		s.push(i);
			}
    	}
    }
    while(!s.empty())
    {
        L[s.top()]=1;
        s.pop();
    }
}
int main()
{
	scanf("%lld",&n);
    while(n!=0)
    {
    	for(int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
    }  
    solve1();
    solve2();
    long long ans=0;
    for(int i=1;i<=n;i++)
	{
		ans=max(ans,a[i]*(R[i]-L[i]+1));
	 }
    printf("%lld\n",ans);
    scanf("%lld",&n);
	}
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章