Early Orders

题意:

给你一个整数列表 x1,x2,,… ,xn 和一个数字 k,它保证从1到 k 的每个 i 至少出现在列表中一次。
现在求一个字典序最小的子序列,子序列有1到k组成

题解:

单调栈求解
我们先预处理,求出每种数最后出现的位置,用数组suf存
比如:这10个数:54321411155
suf[1] = 8
suf[2] = 4
suf[3] = 3
suf[4] = 6
suf[5] = 10
我们设一个栈,每次读入新的数据x(第i位)时与栈顶元素top比较,如果栈顶元素大于x,且suf[top]>i,我们就将栈顶弹出,直到该条件不满足,或者栈为空,然后将数据x存入栈内
原因:
如果suf[top]>i,说明在当前x的后面还会有和栈一样大小的数,而且栈顶大于x,我们为了让字典序更小,就可以用组合x top代替 top x
就比如说:5 4 5,
我们栈顶为5(第一个元素),读入第二个元素4,4比5小,且4之后还有5(第三个元素),说明可以组成字典序更小的4 5,而不是5 4,所以将5(第一个元素)弹出,将4存入

代码:

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
inline int read(){
    
    
   int s=0,w=1;
   char ch=getchar();
   while(ch<'0'||ch>'9'){
    
    if(ch=='-')w=-1;ch=getchar();}
   while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();//s=(s<<3)+(s<<1)+(ch^48);
   return s*w;
}

const int maxn=2e5+9;
ll a[maxn];
int vis[maxn];
int st[maxn],suf[maxn];
ll n,m;
int main(){
    
    
	cin>>n>>m;
	for(int i=1;i<=n;i++) a[i]=read();
	int s = 0;
	for(int i=1;i<=n;i++) suf[a[i]] = i;
	for(int i=1;i<=n;i++){
    
    
		if(vis[a[i]]) continue;
		while(s && a[i]<=a[st[s]] && suf[a[st[s]]]>i) 
		//如果栈顶元素大于当前元素,且栈顶元素后面还会出现 
			vis[a[st[s--]]] = 0;//出栈,并标记为空 
		
		st[++s] = i;
		vis[a[i]] = 1;
	}
	for(int i=1;i<=m;i++)
		printf("%lld ",a[st[i]]);
	printf("\n");
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_35975367/article/details/115284856