1. 程式人生 > >單調棧(poj -- 2559)

單調棧(poj -- 2559)

單調棧,顧名思義就是說棧內的元素,按照某種方式排序下,必須是單調的。如果新入棧的元素破壞了單調性,就彈出棧內元素,知道滿足單調性。

它可以很方便地求出某個數的左邊或者右邊第一個比它大或者小的元素,而且總時間複雜度O(N)。

1. 題目大意:連結

給出一個柱形統計圖(histogram), 它的每個專案的寬度是1, 高度和具體問題有關。 現在程式設計求出在這個柱形圖中的最大面積的長方形。

例如: 

<span style="font-size:18px;">7 2 1 4 5 1 3 3</span>

7表示柱形圖有7個數據,分別是 2 1 4 5 1 3 3, 對應的柱形圖如下,最後求出來的面積最大的圖如右圖所示。

2. 分析:

要想找到裡面的最大的面積,一定會有這麼一種情況,得出的矩形的高度一定為所包含的某一個高度一致的。所以我們可以對某一個柱子的高度為標準,儘量的向兩頭擴充套件,這樣就可以找出以它高度為標準的,幷包含它本身的最大矩形。然後對每一個柱子都做類似的工作,最後挑出裡面最大的矩形。

OK,單從上述所說,一定會有重複的工作,如何剔除重複工作呢?而且什麼叫做盡量向兩頭擴充套件呢?

重複工作之後再說。先說什麼叫儘量向兩頭擴充套件。

如:2 1 4 

第一個:2,以2為高度為準,向左右兩頭擴充套件,它只能想右擴充套件,因為1比2低了,就不可能擴充套件到1了。寬度只有1。

第二個:1,以1為高度為準,向左右兩頭擴充套件,向左,因為2比1高可以擴充套件過去;向右,因為4也高於1所以擴充套件到4,這樣以1為高度的矩形寬為3了;

第三個:4,以4為高度為準,向左右兩頭擴充套件,向左,因為4比1、2都高,顯然不可以擴充套件過去,這樣以4為高度的矩形寬為1了。

所以要將其擴充套件過去,必須高度不低於當前擴充套件點的高度。

所以如果我們從第一個開始計算到第n個,計算到 i 時,如果我們可以快速的找出左邊第一個(這裡第一的意思是離i最近)比 i 的高度小的,就可以完成了向左擴充套件的工作,而向右擴充套件,我們本來就是一直向右走,所以直接擴充套件。這時候就輪到:單調棧出場了!

一直保持單調遞增的棧。

思考剛才的例子:

2進棧,左邊無比其小的元素,記錄其最左擴充套件位置為1

1準備進棧,因為其進棧就破壞了單調棧的性質(2>1),這時2要出棧,因為1<2也說明了2不可能向右擴充套件,出棧,計算以2為準的矩形 2*1,然後1才進棧。1進棧前,發現其前一個元素,既是當前的棧頂2,比1高,而且2的左擴充套件從位置1開始,所以1也有理由從2的左起始位置開始(注意:2比1高),所以2出棧後,1進棧,其左擴充套件應為2的左擴充套件位置1;

4準備進棧,因為4>1直接進棧,其左擴充套件位置只能為3了。

最後要清空棧:4退棧,以為是最右的了,這此時右擴充套件只能為3了。左右擴充套件都為3,即是其本身,矩形為4*1;記錄其位置以備後需;

1退棧,最右擴充套件只能是上一個退棧的元素位置,因為其高度比1高(單調棧的性質),所以利用剛才記錄的位置,1的左右擴充套件就為1,3了,矩形1*3;


3. 具體操作:

建立一個單調遞增棧,所有元素各進棧和出棧一次即可。每個元素出棧的時候更新最大的矩形面積。

設棧內的元素為一個二元組(x, y),x表示矩形的高度,y表示矩形的寬度。

如何壓棧並更新呢?

① 如果當前元素比棧頂元素大或者棧為空,則直接壓棧(x,1);

② 如果當前元素小於等於棧頂元素,則退棧,直到 當前元素大於棧頂元素或者棧為空時,壓棧。

③在退棧的過程中要進行最大面積和累積寬度的更新:

例:

若原始矩形高度分別為2,1,4,5,1,3,3

高度為2的元素進棧,當前棧為(2,1)

高度為1的元素準備進棧,但必須從棧頂開始刪除高度大於或等於1的矩形,因為2已經不可能延續到當前矩形。刪除(2,1)這個元素之後,更新最大矩形面積為2*1=2,然後把它的寬度1累加到當前高度為1的準備進棧的矩形,然後進棧,當前棧為(1,2)

高度為4的元素進棧,當前棧為(1,2) (4,1)

高度為5的元素進棧,當前棧為(1,2) (4,1) (5,1)

高度為1的元素準備進棧,刪除(5,1)這個元素,更新最大矩形面積為5*1=5,把1累加到下一個元素,得到(4,2),刪除(4,2),更新最大矩形面積為4*2=8,把2累加到下一個元素,得到(1,4),1*4=4<8,不必更新,刪除(1,4),把4累加到當前準備進棧的元素然後進棧,當前棧為(1,5)

高度為3的元素進棧,當前棧為(1,5) (3,1)

高度為3的元素準備進棧,刪除(3,1),不必更新,把1累加到當前準備進棧的元素然後進棧,當前棧為(1,5) (3,2)

把餘下的元素逐個出棧,(3,2)出棧,不必更新,把2累加到下一個元素,當前棧為(1,7),(1,7)出棧,不必更新。棧空,結束。

最後的答案就是8。

程式碼1:直接用棧

#include <iostream>
#include <stack>
#include <cstdio>
using namespace std;

struct Node
{
    long long val;
    long long len;
};

stack<Node> s;

int main()
{
    long long temp,Max,n,i,m;
    Node q;
    while(scanf("%I64d",&n)!=EOF)
    {
        if(n==0) break;
        Max=0;
        for(i=0;i<n;i++)
        {
            scanf("%I64d",&q.val);
            q.len=1;
            temp=0;
            if(s.empty())
            {
                s.push(q);
            }
            else if(q.val<=(s.top()).val)
            {
                while(!s.empty()&&q.val<=(s.top().val))
                {
                    (s.top()).len+=temp;
                    m=(s.top()).val*(s.top()).len;
                    if(m>Max) Max=m;
                    temp=(s.top()).len;
                    s.pop();
                }
                q.len+=temp;
                s.push(q);
            }
            else
                s.push(q);
        }
        temp=0;
        while(!s.empty())
        {
            (s.top()).len+=temp;
            m=(s.top()).val*(s.top()).len;
            if(m>Max) Max=m;
            temp=(s.top()).len;
            s.pop();
        }
        cout<<Max<<endl;
    }
}


程式碼2:用陣列模擬棧

#include <iostream>

<span style="font-size:14px;">#include <cstdio>
using namespace std;

#define maxn 100005
pair<long long,long long> p[maxn];

int main()
{
    long long n,num,k,i,Max,m,prev;

    while(scanf("%I64d",&n)!=EOF)
    {
        if(n==0) break;
        Max=0;
        scanf("%I64d",&p[0].first);  //first表示高度,second表示寬度
        p[0].second=1;
        k=0;
        for(i=1;i<=n;i++)
        {
            if(i<n)scanf("%I64d",&num);
            else num=-1;
            prev=0;
            while(k>=0&&num<=p[k].first)
            {
                p[k].second+=prev;
                m=p[k].first*p[k].second;
                if(m>Max) {Max=m;}
                prev=p[k].second;
                k--;
            }
            if(num!=-1)
            {
                    p[++k].second=prev+1;
                    p[k].first=num;
            }
        }
        printf("%I64d\n",Max);

    }

    return 0;
}
</span>

相關推薦

單調poj -- 2559

單調棧,顧名思義就是說棧內的元素,按照某種方式排序下,必須是單調的。如果新入棧的元素破壞了單調性,就彈出棧內元素,知道滿足單調性。 它可以很方便地求出某個數的左邊或者右邊第一個比它大或者小的元素,而且總時間複雜度O(N)。 1. 題目大意:連結 給出一個

HDU 1506 單調 dp思想

Description A histogram is a polygon composed of a sequence of rectangles aligned at a common base line. The rectangles have equal

【Codeforces 631C 】Report單調,思維模擬

題幹: Each month Blake gets the report containing main economic indicators of the company "Blake Technologies". There are n commodities pr

HDU 5875 Function單調+線上倍增法

Description 一個長度為n的序列Ai,m次查詢,每次查詢求f(l,r),其定義如下: Input 第一行一整數T表示用例組數,每組用例首先輸入一整數n表示序列長度,之後n個整數Ai表示該序列,然後輸入一整數m表示查詢數,最後m行每行兩個整數l

單調POJ2559

name have integer align set base namespace .org ase Description A histogram is a polygon composed of a sequence of rectangles aligned at

[GX/GZOI2019]與或和單調+按位運算

++ calc ide namespace class db4 names 圖片 d+ 首先看到與或,很顯然想到按照位拆分運算。然後就變成了0/1矩陣,要使矩陣在當前位與為1,則矩陣全為1,如果是或為1,則是矩陣不全為0,然後求全為0/1的矩陣個數即可。記錄c[i][j]表

[luoguP1044] 數論?

%d nbsp targe pid scanf ble urn 數論 pre 傳送門 卡特蘭數 代碼 #include <cstdio> int n; long long f[20]; int main() { int

狀壓dpNOI 2001POJ 1185 炮兵陣地

上下 數據 enter 能夠 sam src max spa 參加 司令部的將軍們打算在N*M的網格地圖上部署他們的炮兵部隊。一個N*M的地圖由N行M列組成,地圖的每一格可能是山地(用"H" 表示),也可能是平原(用"P"表示),如下圖。在每一格平原地形上最多可以布置一支炮

洛谷P1044 Catalan數

tick sticky catalan 可能 out pre 需要 題目 span P1044 棧 題目背景 棧是計算機中經典的數據結構,簡單的說,棧就是限制在一端進行插入刪除操作的線性表。 棧有兩種最重要的操作,即pop(從棧頂彈出一

B - Brainman POJ - 1804

bsp include man ace blog 求逆 urn str iostream - 題目大意 給出一串數字,問能是它為順序排列的最小交換數字方式。 - 解答思路 利用歸並排序來求逆序數(註意數組的大小就行了)。 - 代碼 #include<

E - Squares POJ - 2002

stream clu ostream clas using 通過 ret for 不同 - 題目大意 有一堆平面散點集,任取四個點,求能組成正方形的不同組合方式有多少。相同的四個點,不同順序構成的正方形視為同一正方形。 - 解題思路 先枚舉兩個相鄰的點,通

G - Best Cow Fences POJ - 2018

class 前綴數組 mes cout () light cin ret ace - 題目大意 給你n個牛的自身價值,讓你找出連續的且數量大於等於F的一段區間,使這段區間內的牛的平均價值最大。 - 解題思路 這道題可以用二分法也可以結合前綴數組來求和來做

B - 昂貴的聘禮 POJ - 1062

pri 標記 ring level -- space () log || - 題目大意 有N個物品,每個物品都有自己的價格,但同時某些物品也可以由其他的(可能不止一個)替代品,這些替代品的價格比較“優惠”,問怎麽樣選取可以讓你的花費最少來購買到物品1。 - 解題思路

E - Wormholes POJ - 3259

ret str edge brush 負環 esp tdi 還要 sin - 題目大意 個人要穿越到未來,但是之後還要回去,並且回去的時間要在他穿越之前。 - 解題思路 我們可以把在蟲洞中的時間看做是負邊權,然後利用bellman-ford算法來判斷有沒有負

每天一道博弈論之“A funny game”poj 2484

log tar 硬幣 -s 獨立遊戲 div size 狀態 http 題意:   n枚硬幣排成一個環,操作為可以選擇相鄰的兩個取走(相鄰指的是最開始相鄰,即不會自動補成環)。問先手勝還是後手勝。 題解:   首先我們考慮1和2,則明顯是先手必勝。  

Sightseeing trip POJ - 1734

order string courier ace ase them offer gin action There is a travel agency in Adelton town on Zanzibar island. It has decided to offer i

Fibonacci【矩陣乘法】POJ 3070

not org emc image const pro input The memcpy Description In the Fibonacci integer sequence, F0 = 0, F1 = 1, and Fn = Fn ? 1 + Fn ? 2 for

Prime DistancePOJ 2689

possible const code ini tin tput strong family name Description The branch of mathematics called number theory is about properties of num

「日常訓練」All FriendsPOJ-2989

any cin memset ace string ear key return ans 題意 分析 代碼 #include <iostream> #include <cstring> #include <algorithm> #defi

Hie with the PiePOJ 3311狀壓DP

red san directly itself ffffff 16px say integer help Description The Pizazz Pizzeria prides itself in delivering pizzas to its customers