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.youkuaiyun.com/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;
}