单调栈题集(题目加代码,持续更新ing...)

1.leetcode496.下一个更大元素I

代码

class Solution {
public:
    vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
          stack<int>s;
          int vis[10005];
          memset(vis,-1,sizeof vis);
          for(int i=nums2.size()-1;i>=0;i--){
              while(!s.empty()&&nums2[i]>=s.top()){
                  s.pop();
              }
              if(s.empty()) vis[nums2[i]]=-1;
              else vis[nums2[i]]=s.top();
              s.push(nums2[i]);
          }
          vector<int>q;
          for(int i=0;i<nums1.size();i++){
              q.push_back(vis[nums1[i]]);
          }
          return q;
    }
};

2.leetcode739. 每日温度

代码

class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& temperatures) {
        vector<int>q;
        stack<int>s;
        for(int i=temperatures.size()-1;i>=0;i--){
            while(!s.empty()&&temperatures[i]>=temperatures[s.top()]){
                s.pop();
            }
            if(s.empty())
            {
                q.push_back(0);
            } 
            else q.push_back(s.top()-i);
            s.push(i);
        }
        reverse(q.begin(),q.end());
        return q;
    }
};

3.leetcode503. 下一个更大元素Ⅱ

代码

class Solution {
public:
    vector<int> nextGreaterElements(vector<int>& nums) {
        int siz=nums.size();
        vector<int>q=nums;
        stack<int>s;
        for(int i=0;i<q.size()-1;i++){
            nums.push_back(q[i]);
        }
        for(int i=q.size()-1;i>=0;i--){
            s.push(q[i]);
        }
        vector<int>ans;
        for(int i=siz-1;i>=0;i--){
            while(!s.empty()&&nums[i]>=s.top()){
                s.pop();
            }
            if(s.empty()){
                ans.push_back(-1);
            }
            else ans.push_back(s.top());
            s.push(nums[i]);
        }
        reverse(ans.begin(),ans.end());
        return ans;
    }
};

4.leetcode 901. 股票价格跨度

代码

class StockSpanner {
public:
    stack<int>p,d;
    int cnt=1;
    StockSpanner() {
        p.push(1e5+5);
        d.push(1);
    }

    int next(int price) {
        cnt++;
        int ans;
        while(!p.empty()&&price>=p.top()){
            p.pop();
            d.pop();
        }
        if(p.empty()) ans=1;
        else ans=cnt-d.top();
            p.push(price);
            d.push(cnt);
        return ans;
    }
};

/**
 * Your StockSpanner object will be instantiated and called as such:
 * StockSpanner* obj = new StockSpanner();
 * int param_1 = obj->next(price);
 */

5.acwing131. 直方图中最大的矩形

#include<bits/stdc++.h>  
using namespace std;
const int N=1e5+5;
#define ll long long
int h[N];
int l[N];
int r[N];
stack<int>s;
int main(){
    int n;
    while(cin>>n&&n){
        for(int i=1;i<=n;i++){
            cin>>h[i];
        }
        while(!s.empty()) s.pop();
        for(int i=1;i<=n;i++){       //找到左边第一个小于它的右边一个位置
            while(!s.empty()&&h[s.top()]>=h[i]){
                s.pop();
            }
            if(s.empty()) l[i]=1;
            else l[i]=s.top()+1;
            s.push(i);
        }
        while(!s.empty()) s.pop();
        for(int i=n;i>=1;i--){       //找到右边第一个小于它的左边一个位置
            while(!s.empty()&&h[s.top()]>=h[i]){
                s.pop();
            }
            if(s.empty()) r[i]=n;
            else r[i]=s.top()-1;
            s.push(i);
        }
        ll ans=0;
        for(int i=1;i<=n;i++){
            ans=max(ans,(ll)h[i]*(r[i]-l[i]+1));
        }
        cout<<ans<<'\n';
    }
    return 0;
}

6.Codeforces Round #618 (Div. 2)E. Water Balance

在这里插入图片描述
在这里插入图片描述
题意:每次可以选一段区间让他们平均分配,最后形成的字典序最小的区间是什么
最后的序列应该是递增的,并且是一块一块的,每一块的值应该相同,因此我们就想如果前面那一块的平均值大于紧接着的后面一个的值,就进行合并,就可以减小前面那一块的平均值。
先贴个错误的(假贪心):
(开始以为右边那一个小于上一次的平均值就会合并,sb了,右边的右边非常小也可以把前面的平均值拖小)

#include<bits/stdc++.h>
using namespace std;
#define pii pair<int,int>
#define pb push_back
#define x first
#define y second
const int N=1e6+5;
double a[N];
double sum[N];
double b[N];
vector<pii>ans;
int main(){
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%lf",&a[i]);
		sum[i]=sum[i-1]+a[i];
	}
    int l=1,r=1;
    a[n+1]=10000000.0;
    while(r<=n){
    	int f=0;
    	while((sum[r]-sum[l-1])/(r-l+1)>=a[r+1]){
    		f=1;
    		r++;
		}
		if(f==0){
			ans.pb({l,r});
			l=r+1;
			r=l;
		}
	}
    for(auto &k:ans){
    	int l=k.x;
    	int r=k.y;
    	double pj=(sum[r]-sum[l-1])/(r-l+1);
    	for(int i=l;i<=r;i++)
		{
			printf("%.10lf\n",pj);
		 } 
	} 
	return 0;
}

正解:
我们现在就要解决上面说的那种问题,对于每一个l,大于它的所有r(l+1,l+2…),区间[l,r]都应该考虑到,因为趋势可能是下一个比前一个大,但是下下个比前一个小,但是n^2处理时间复杂度肯定不行,这种问题,我们就想,三个数x,y,z,倒着看,如果z<y,那z和y平均可以优化y,接着看是否可以优化x。也就是说,我们用一个单调栈,栈里面存的是之前已经最优化的结果,每进来一个数a[i],我们查看它能否优化栈顶的平均值,如果说现在这个a[i]与栈顶元素取平均值还能让栈顶元素更小,那就把a[i]与栈顶元素取平均值,一直优化栈顶元素,直到不能优化栈顶元素为止,再把这个新的平均值放入栈。但是栈存放的不是这个值本身,而是每一段区间的右端点的下标。
表达能力很差,放代码:

#include<bits/stdc++.h>
using namespace std;
#define pii pair<int,int>
#define pb push_back
#define x first
#define y second
const int N=1e6+5;
double a[N];
double sum[N];
double b[N];
int sta[N];
int top;
int main(){
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%lf",&a[i]);
		sum[i]=sum[i-1]+a[i];
	}
    for(int i=1;i<=n;i++){
    	double num=a[i];
    	while(top&&(num+sum[sta[top]]-sum[sta[top-1]])/(i-sta[top-1])<=a[sta[top]]){
    		num=num+sum[sta[top]]-sum[sta[top-1]];
    		//cout<<i<<' '<<sta[top]<<' '<<sta[top-1]<<' '<<num<<'\n';
    		top--;
		}
		if(top){
			a[i]=num/(i-sta[top]);
		}
		else{
			a[i]=num/i;
		}
		sta[++top]=i;
	}
	vector<int>ans;
	while(top){
		ans.pb(sta[top]);
		top--;
	}
	reverse(ans.begin(),ans.end());
	int ls=0;
	for(auto &k:ans){
		double pj=(sum[k]-sum[ls])/(k-ls);
		for(int i=ls+1;i<=k;i++){
			printf("%.10lf\n",pj);
		}
		ls=k;
        //cout<<k<<'\n'; 
	}
	return 0;
}

7.Early Orders

题意:
是给你n个数ai,每个数字在1-k之间,要求选一个ai的子序列是k的字典序最小的全排列。n<=1e5、
题解:单调栈思想,对于遍历到的某个数ai,如果ai之前在栈里面,就不管这个数了,如果之前不存在并且ai小于栈顶元素并且第i个数后面的位置还有栈顶元素出现,就一直弹出栈顶元素,再把ai入栈
代码:

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int a[N];
map<int,int>mp;
int main(){
    int n,k;
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        mp[a[i]]=i;
    }
    stack<int>s;
    set<int>p;
    for(int i=1;i<=n;i++){
        if(p.count(a[i])) continue;
        while(!s.empty()&&(a[i]<s.top()&&mp[s.top()]>i)){
            p.erase(s.top());
            s.pop();
        }
            s.push(a[i]);
            p.insert(a[i]);
    }
    vector<int>ans;
    while(!s.empty()){
        ans.push_back(s.top());
        s.pop();
    }
    reverse(ans.begin(),ans.end());
    for(auto &h:ans){
        printf("%d ",h);
    }
    puts("");
    return 0;
}

8.Codeforces Round #710 (Div. 3) G. Maximize the Remaining String

思路和上面那题一模一样
代码:

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
map<char,int>mp;
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
    	string a;
    	cin>>a;
    for(int i=0;i<a.size();i++){
        mp[a[i]]=i;
    }
    stack<char>s;
    set<char>p;
    for(int i=0;i<a.size();i++){
        if(p.count(a[i])) continue;
        while(!s.empty()&&(a[i]>s.top()&&mp[s.top()]>i)){
            p.erase(s.top());
            s.pop();
        }
            s.push(a[i]);
            p.insert(a[i]);
    }
    string ans;
    while(!s.empty()){
        ans+=s.top();
        s.pop();
    }
    reverse(ans.begin(),ans.end());
    cout<<ans<<'\n';
	}
    return 0;
}

9.2020HDU多校第一场 Leading Robots

题意:就是T组数据,每一组有n个机器人,有各自的初始位置p和加速度a,问过程中当过第一名的机器人有多少个,第一名是unique的
思路:
首先我们知道,如果一个机器人初始位置和加速度都大于另一个,那么另一个机器人永远不可能当第一名,那我们现在先按照加速度,再按照初始位置排个序,保证后面的机器人加速度越来越大,用栈存储,当要进栈那个机器人的初始位置也更大时,说明栈顶机器人不可能当第一名,弹出栈,也就是保证了栈里面后面进来的机器人的初始位置都要小于前面进栈的机器人,那这样就可以了吗,也不是,我们想想,这样一幅图
在这里插入图片描述
c入栈前,里面有a,b,但是c超过b的时间在b超过a之前,是不是说明b还没超过a,它就先被c超过了,b永远不可能成为第一名,所以我们就得考虑即将进站的机器人超过栈顶元素的时间和栈顶机器人超过栈顶下一个机器人的时间,进行一个比较,判断栈顶机器人是否弹出栈,时间怎么判断呢,这不是二次的吗,那就直接用t^2比较就行了,除法换乘法可以不用double
在这里插入图片描述
注意第一名是unique的条件,我wa了几发都在这里了,单调栈最后存的机器人确实都无重复,那是因为算法过程中筛掉了,但是筛出去的可能与栈里面留下的机器人p和a都相同,也就是说本来有多个人有相同的p和a,但是最后栈里面是一个,那这样的机器人我们也不要,因为无法保证第一名唯一的条件
代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pll pair<ll,ll>
const int N=1e5+5;
struct node{
	ll p;
	ll a;
}q[N];
bool cmp(node a1,node a2){
	if(a1.a!=a2.a) return a1.a<a2.a;
	else return a1.p<a2.p;
}
int sta[N];
bool check(node a,node b,node c){
	return ((c.p-b.p)*(a.a-b.a)-(b.a-c.a)*(b.p-a.p))<=0;
}
map<pll,int>mp;
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
    	int n;
    	scanf("%d",&n);
    	int cnt=0;
    	mp.clear();
    	for(int i=1;i<=n;i++){
    		ll x,y;
    		scanf("%lld%lld",&x,&y);
    		q[++cnt]={x,y};
    		mp[{x,y}]++;
		}
		sort(q+1,q+1+cnt,cmp);
		int top=0;
		for(int i=1;i<=n;i++){
			while(top&&q[i].p>=q[sta[top]].p){
				top--;
			}
			while(top>=2&&check(q[sta[top-1]],q[sta[top]],q[i])){  //比较时间 
				top--;
			}
			sta[++top]=i;
		}
		int ans=0;
		for(int i=1;i<=top;i++){
			if(mp[{q[sta[i]].p,q[sta[i]].a}]==1) ans++;
		}
		printf("%d\n",ans);
	}
    return 0;
}

10.2021牛客暑期多校训练营2 K.Stack

贴一篇写得不错的题解:
https://frozenguardian.blog.csdn.net/article/details/118963197
要注意的几点就是说:首先排列嘛,元素都不同,然后新来的一个小于栈里面的才会弹出栈,大于的话,b[i]就会在上一个基础上增加,那现在栈模拟一下,栈存的是下标,而不是b[i],因为栈里面b[i]是递增的,b[i]如果小于等于栈元素的个数,比如1,2,3,4,5 b[i]=3,那现在b[u]=5,这个的a[u]必然大于a[i],3,4一样的,b[u]=2,那a[u]<a[i],现在每个数的大小关系我们知道,谁比谁大,谁比谁小,要保证这些大小关系成立,小的向大的连一条边,然后跑一个拓扑图就行。但是scanf只能过96.8%左右,还得用read读一下。。。。。

11.hdu 2021多校第一场 Maximal submatrix

题意:给出一个n行m列的矩阵,每个位置有一个值,求最大的子矩阵,满足子矩阵每一列的元素值从上到下非递减
思路:对于每一列,求以第i行为底的非递减序列最长是多长,现在要求题目那种矩阵,以从1到n每行为底考虑,然后就是直方图矩阵那种问题,注意每个矩阵每列都要满足非递减条件,那求h[[i]时就得考虑左右两边第一个小于h[i]的位置而不是大于h[i],这是与上面那个题不同的一点。
代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=2e3+5;
int a[N][N];
int h[N];
int l[N];
int r[N];
int f[N][N];
int main(){
	int t;
	scanf("%d",&t);
	while(t--){
		int n,m;
		scanf("%d%d",&n,&m);
		memset(f,0,sizeof f);
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++)
			{
				scanf("%d",&a[i][j]);
			}
		}
		for(int j=1;j<=m;j++) f[1][j]=1;
		for(int i=1;i<=m;i++)
        {
            for(int j=2;j<=n;j++)
            {
                if(a[j][i]>=a[j-1][i])
                f[j][i]=f[j-1][i]+1;
                else f[j][i]=1;
            }
        }
		int ans=0;
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++) h[j]=f[i][j];
		    stack<int>s;
		    for(int j=1;j<=m;j++){
			    while(!s.empty()&&h[j]<=h[s.top()]){
				s.pop();
			}
			if(s.empty()){
				l[j]=1;
			}
			else l[j]=s.top()+1;
			s.push(j);
		 }
		while(!s.empty()) s.pop();
		for(int j=m;j;j--){
			while(!s.empty()&&h[j]<=h[s.top()]){
				s.pop();
			}
			if(s.empty()) r[j]=m;
			else r[j]=s.top()-1;
			s.push(j);
		}
		int res=0;
		for(int j=1;j<=m;j++){
			res=max(res,h[j]*(r[j]-l[j]+1));
		} 
		ans=max(ans,res);
		}
		printf("%d\n",ans);
	}
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

eeemmm123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值