單調棧解木板倒水問題(單調棧的簡單應用)

2021-07-10 04:23:47 字數 3923 閱讀 5075

題目描述:

地上從左到右豎立著 n 塊木板,從 1 到 n 依次編號,如下圖所示。我們知道每塊木板的高度,在第 n 塊木板右側豎立著一塊高度無限大的木板,現對每塊木板依次做如下的操作:對於第 i 塊木板,我們從其右側開始倒水,直到水的高度等於第 i 塊木板的高度,倒入的水會淹沒 ai 塊木板(如果木板左右兩側水的高度大於等於木板高度即視為木板被淹沒),求 n 次操作後,所有 ai 的和是多少。如圖上所示,在第 4 塊木板右側倒水,可以淹沒第 5 塊和第 6 塊一共 2 塊木板,a4 = 2。

解析:從對每塊木板的操作:

對於第 i 塊木板,我們從其右側開始倒水,直到水的高度等於第 i 塊木板的高度,倒入的水會淹沒 ai 塊木板(如果木板左右兩側水的高度大於等於木板高度即視為木板被淹沒)

我們可以思考,要怎麼樣倒入的水才會停止"淹沒" 木板呢?其實我們看圖示就很清楚了,要使水不再往右延伸的話,那麼必然要遇到乙個比當前操作的木板更高的木板。

所以這道題轉換成整型陣列有n個元素,然後找到每個元素右邊第乙個大於該元素的數,並記錄下標temp_id,那麼第 i 塊木板(下標為i)淹沒木板的塊數即為:

ai = temp_id - i - 1;    //從圖示中即可計算出ai的值

如果這道題n的範圍比較小的話,那麼可以雙重for迴圈直接暴力遍歷即可,時間複雜度為o(n^2),但是我們僅僅就滿足於這樣的**嗎?有沒有更好的辦法能夠解決這個問題?首先我們從轉化後的題設入手,每個元素都要找到右邊第乙個大於該元素的數

那麼還記得單調棧的性質3嗎?單調棧的性質3:

使用單調棧可以找到元素向左遍歷第乙個比他小的元素,也可以找到元素向左遍歷第乙個比他大的元素。

對於這道題呢(考慮給定的測試用例),首先我們考慮從左到右依次將陣列中的資料元素壓入棧中,當遍歷到第乙個元素時,此時棧為空,所以第乙個元素先要壓入棧中,然後到調整部分,遍歷到第二個元素,將第二個元素與棧頂相比較,如果當前遍歷到的元素大於或者等於棧頂元素,棧頂元素找到了其右邊第乙個大於他的元素,那麼這樣的話,其實就已經解決了題設的問題,

然後將ans(最後的結果)加上當前元素的下標 - 棧頂元素的下標 - 1,隨後棧頂元素出棧(表示該棧頂元素已經處理完畢),然後重複上述過程,繼續向左遍歷,直到找到第乙個比他大的元素,將其壓入棧中,成為新的棧頂,停止遍歷,處理下乙個元素,而如果當前遍歷到的元素小於棧頂元素,說明當前遍歷到的元素無法再向左邊延伸,因此將當前遍歷到的元素壓入棧中。

然後繼續向後遍歷陣列中的資料元素,並按照上述方式處理當前遍歷到的元素以及棧頂元素。

每次操作結束之後,仔細觀察已經壓入棧中的元素,你會發現,從棧底到棧頂,木板的高度始終是單調遞減的。

其實這就轉化成了一道利用單調棧維護的題目了,即每塊遍歷到的木板尋找屬於「自己」的位置(包含最後一塊無限長的木板),而單調棧維護的時間複雜度為o(n),所以在資料範圍比較大的情況下,也不用擔心程式執行時間過長的問題。

圖示分析:

圖示中總共有n塊木板,第n+1塊是一塊高度為無限大的木板,按照上述思路對a1-a6這六塊木板進行處理。

一開始的時候,ans賦初始值為0.

1.遍歷到a1時,此時棧為空,所以將a1壓入棧中,此時的棧頂元素為a1;

2.然後繼續向右遍歷到a2時,a2 > a1,所以此時記錄第1塊木板淹沒木板的塊數sum1 = 2 - 1 - 1 = 0,然後ans += sum1,此時的ans = 0

然後a1出棧,此時棧為空,因此a2入棧,此時新的棧頂元素為a2

3.隨後遍歷至a3,a3 < a2,因此a3壓入棧中,成為新的棧頂。此時新的棧頂元素為a3

4.隨後遍歷至a4,a4 > a3,

所以此時記錄第3塊木板淹沒木板的塊數sum3 = 4 - 3 - 1 = 0,然後ans += sum3,此時的ans = 0

然後a3出棧,繼續向左遍歷,發現a2,而a2 > a4,找到了第乙個比他大的元素,停止遍歷。將a4壓入棧中,

此時新的棧頂元素為a4

5.隨後遍歷至a5,a5 < a4,因此a5壓入棧中,成為新的棧頂。

此時新的棧頂元素為a5

6.隨後遍歷至a6,a6 > a5,

所以此時記錄第5塊木板淹沒木板的塊數sum5 = 6 - 5 - 1 = 0,然後ans += sum5,此時的ans = 0

然後a5出棧,繼續向左遍歷,發現a4,而a4 > a6,找到了第乙個比他大的元素,停止遍歷。將a6壓入棧中,此時新的棧頂元素為a6

7.然後再繼續向後遍歷,發現了那塊高度無限大的木板,說明此時遍歷結束(當前遍歷到了第n+1塊木板)

那麼,到現在處理完畢了嗎?很明顯是沒有的,題設要求的是n 次操作後,所有 ai 的和是多少,而當我們遍歷結束的時候,只記錄了其中三塊木板淹沒木板的塊數,而棧中從棧底到棧頂依次還有a2,a4,a6(說明這三塊木板均為處理),所以呢,在for迴圈遍歷的語句之下,還要加上乙個迴圈判定

此時棧是否為空,而如果棧不為空,而從棧底到棧頂,木板的高度始終是單調遞減的。說明當前還在棧中的資料元素,右邊第乙個大於該元素的數都是第n+1個數,並且第n+1個元素的位置是「棧底」,所以我們只需要從棧頂開始一一處理剩下的元素即可,直到棧空,說明全部元素處理完畢。

所以接下來是處理剩下的元素,從棧頂開始,有sum6 = 6+1-6-1 = 0,ans += sum6,此時ans = 0,a6處理完畢,出棧;

然後sum4 = 6+1-4-1 = 2,ans += sum4,此時ans = 2,a4處理完畢,出棧;

最後是a2,sum2 = 6+1-2-1 = 4,ans += sum2,此時ans = 6,a2處理完畢,出棧;

迴圈判斷此時棧空,跳出迴圈,截止到此時,所有元素處理完畢。

輸出結果,ans = 6.

這道單調棧的試題需要注意的是,當遍歷完陣列中所有的資料元素時,也就是當前遍歷到的資料元素到了第n+1個,就像上圖圖示所示的,第n+1塊木板的高度是無限大的,那麼這塊木板是可以「延伸」到棧底的,所以當遍歷結束之後,只需要利用這塊木板一一處理剩下的元素即可。

完整**實現:

#include#includeusing namespace std;

class node ;

templateclass stack

~stack()

bool push(const type &element)

top_index++;

urls[top_index] = element;

return true;

}bool pop()

top_index--;

return true;

}type top()

bool empty() else

}};int main()

stack.push(temp);

}while(!stack.empty())

cout << ans << endl;

return 0;

}

如有錯誤,還請指正,o(∩_∩)o謝謝

倒水 water (單調棧)

題目描述 有一天,你拿來了乙個長方形的容器,然後開始往裡面倒水。這個長方形的容器由n個單元組成,每乙個單元都有乙個高度。現在你想知道,要想讓水沒過第i個位置,他至少需要倒進去多少單位的水。輸入 第一行乙個整數n。接下來一行n個整數,第i個整數hi表示第i個位置的高度。輸出 為了減少輸出量,假設要想讓...

單調棧問題

從名字上就聽的出來,單調棧中存放的資料應該是有序的,所以單調棧也分為單調遞增棧和單調遞減棧 單調遞增棧 棧中資料出棧的序列為單調遞增序列 單調遞減棧 棧中資料出棧的序列為單調遞減序列 ps 這裡一定要注意所說的遞增遞減指的是出棧的順序,而不是在棧中資料的順序 單調棧板子題 這題是簽到題,很明顯就是乙...

單調棧問題

單調棧能夠求得該數左邊離他最近的大於 小於的數 其他問題也可以轉換為此問題 例如 求 a i 左邊第乙個小於a i 的數 對於a i 每次入棧如果有大於等於a i 的數,直接出棧 證明 如果求左側第乙個小於a i 的數 如果a i 入棧後左側有大於a i 的數,則那個數不僅是大值而且還出現的比較早,...