本文已参与[新人创作礼]活动,一起开启掘金创作之路
Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
题目描述
这是3月22日代码源div2的每日一题。
知识点:单调栈
字典序最小 - 题目 - Daimayuan Online Judge
从序列 M 个数中顺序选出 N 个不同的数, 使得这 N 个数的字典序最小。
输入格式
第一行两个整数 M, N分别表示序列长度,顺序选取数据的个数 (其中1<N≤M≤10^6)。
接下来 M 行,第 i 行输入为第 ai,表示序列 M 中第i 个数,其中 1≤ai≤N, 数据保证 [1,N] 范围内每个数至少出现一次。
输出格式
输出 N 个数, 用空格隔开, 表示最小字典序 (最后一个输出不能有多余空格)。
样例输入
6 3
3
2
1
3
1
3
样例输出
2 1 3
题目说明
求解的最小字典序不必在序列 M 中连续。
问题分析
这题我们想要得到最小的字典序,那前面的数都要尽可能小,随着遍历数组我们要更新更小的元素,同时把大的元素从记录的序列中移除,这一性质就很适合我们单调栈。
我们先预处理一下,用一个数组f先记录每个数的出现次数,然后用一个栈遍历数组,每遍历到一个数,数组f里这个数的出现次数--,如果栈为空,就把遍历到的元素入栈,并且用一个数组记录当前栈内的元素;如果栈不为空,而且当前元素没有在栈内,就用当前元素和栈顶元素比较一下,如果栈顶元素在数组的出现次数不为0(数组后面还会出现),就把这个元素出栈,然后继续用当前元素和新的栈顶元素对比,直到新的栈顶元素在数组后面不再出现(如果此时再删,那这个数就永远丢失了),或者栈顶元素小于当前元素是停止出栈,把元素入栈。如果一开始当前元素已经在栈内了,那就不用进行出栈操作(我们出栈是为了把小的数尽可能放前面,既然这个元素已经有在栈内了就不用在出栈了),最后把栈内元素反向输出就行。
AC代码
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include<numeric>
#include<string>
#include<string.h>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>
#include<iomanip>
#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#define endl '\n';
typedef long long ll;
typedef pair<ll, ll>PII;
const int N = 1e6 + 50;
int f[N];
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int n,m;
cin >> n >> m;
vector<int>v(n),mymap(N+1);
for (int i = 0; i < n; i++)
{
cin >> v[i];
f[v[i]]++;
}
stack<int>sta;
sta.push(v[0]);
f[v[0]]--;
mymap[v[0]] = 1;
for (int i = 1; i < n; i++)
{
while (!sta.empty() && sta.top() > v[i] && f[sta.top()] > 0 && mymap[v[i]] != 1)
{
mymap[sta.top()] = 0;
sta.pop();
}
if (mymap[v[i]] == 0)
{
sta.push(v[i]);
mymap[v[i]] = 1;
}
f[v[i]]--;
}
vector<int>res;
while (!sta.empty())
{
res.push_back(sta.top());
sta.pop();
}
cout << res[m - 1];
for (int i = m - 2; i >= 0; i--)cout << " " << res[i] ;
return 0;
}