單調棧基本應用

1:給一段序列,讓你求每個數的右邊 第一個比它大的數 離它的距離。

例題:Bad Hair Day

經典的單調棧題目了,把題意轉換成,每頭牛被看了多少次,加起來就好了。所以我們要維護一個單調增的棧。(我們一般稱棧頂到棧底遞增的棧爲單調增的棧)

代碼如下:(這題數據有點問題,ans要開long long 才能過)

stack<int> st;

int main()
{
	int n; scanf("%d", &n);
	ll ans = 0;
	rep(i, 1, n){
		int x; scanf("%d", &x);
		while(!st.empty() && st.top() <= x) st.pop();
		ans += st.size();
		st.push(x);
	}
	printf("%lld\n", ans);
	return 0;
}

2,給你一個序列,讓你找出這樣的序列,使子序列中的最小值乘子序列長度最大

首先可以想到,我們的答案肯定是在一個元素的值 乘 (最多往左延伸或者往右延伸的距離) 中取,要直接維護這個東西的複雜度是O(n ^ 2)的,我們可以用單調棧來降低複雜度。

容易想到我們要維護的是一個單調減的棧(棧頂到棧底是遞減的),這樣我們就能保證棧中的每一個元素,肯定比它下面的大,那麼每次進來一個比棧頂小的就可以取答案了,因爲這時候一定能保證  (當前元素 與 棧頂元素之間的所有元素) 都比當前元素大,並且比s.top()大。這裏的彈出就意味着這個元素的“使命”已經結束了,因爲來了個更小的,限制了它的延伸,所以計算一下答案維護最大值就彈出去,維護棧的單調性。

棧爲空說明左邊沒有比它小的元素,就可以直接乘i,不空就乘i - s.top() - 1這個區間 ,這個區間一定是合法的。

這樣子,對於一個數,如果能往左邊延伸(至少相鄰的左邊比它大),就能直接求得往左延伸的答案,如果能往右邊延伸(同理),在右邊遇到第一個比它小的時候也會更新到,所以答案是一定正確的。

一個小細節就是在後面加一個高度爲0的點,否則可能會找不到正確答案。

84. 柱狀圖中最大的矩形

class Solution {
	public:
		int largestRectangleArea(vector<int>& heights) {
			int Max = 0;
			stack<int> st;
			heights.push_back(0);
			for(int i = 0; i < heights.size(); i++) {
				while(!st.empty() && heights[st.top()] >= heights[i]) {
					int now = st.top(); st.pop();
					Max = std::max(Max, heights[now] * (st.empty() ? i : (i - st.top() - 1)));
				}
				st.push(i);
			}
			return Max;
		}
};

力扣的接口式代碼讓我有點難受。。

 

3,給你一個序列,求出一個連續子序列,使得子序列的最小值乘子序列長度和最大

我們現在只研究沒有負數的情況,有負數看這https://blog.csdn.net/swunHJ/article/details/89462086

一樣的,單調棧的想法還是由貪心來的,貪心就是取每一個元素的xxx不想打字了很好想n ^ 2

怎麼用單調棧去優化這個東西

顯然要求出來兩個數組,l[i]表示i位置的元素最多往左延伸多少,r[i]表示往右的

先考慮l數組

其實已經比較好想了,維護一個單調增的棧,如果來了個小的一直彈,否則入棧

僞代碼都能寫出來了,

while(!st.empty() && a[i] <= a[st.top()]) st.pop();

if(st.empty()) l[i] = 1;

else l[i] = st.top() + 1;

很好理解不多解釋了,相同的辦法倒着循環我們也能做出來r數組。

感覺有更好一點的辦法我也不想思考了,這樣就行了

例題:Feel Good

AC Code:

int n;
int a[maxn], l[maxn], r[maxn];
ll sum[maxn];
stack<int> st;

int main()
{
	scanf("%d", &n);
	rep(i, 1, n) scanf("%d", a + i);
	rep(i, 1, n) sum[i] = sum[i-1] + a[i];
	//a[++n] = -1;
	rep(i, 1, n){
		while(!st.empty() && a[i] <= a[st.top()]) st.pop();
		if(st.empty()) l[i] = 1;
		else l[i] = st.top() + 1;
		st.push(i);
	}
	while(!st.empty()) st.pop();
	Rep(i, n, 1){
		while(!st.empty() && a[i] <= a[st.top()]) st.pop();
		if(st.empty()) r[i] = n;
		else r[i] = st.top() - 1;
		st.push(i);
	}
	ll ans = -1;
	int ansl, ansr;
	rep(i, 1, n){
		ll cur = 1ll * (sum[r[i]] - sum[l[i]-1]) * a[i];
		if(cur > ans) ans = cur, ansl = l[i], ansr = r[i];
	}
	printf("%lld\n%d %d\n", ans, ansl, ansr);
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章