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的點,否則可能會找不到正確答案。
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;
}