2022.3.22 代码源每日一题div1 #608. 字典序最小

首先考虑到选择数字应该从第一个数字向后逐个确定
那么每一个数字的可选范围是哪些呢?
比如开始的时候,我们要选择\(n\)个数,那我们应该保证第一个选择之后,第一个数对应位置的右边还有\(n-1\)个与已选数字不同的数字
用\(cnt[a[i]]\)表示每个\(a[i]\)出现的次数
首先,我们要保证每次选的数字与已选数字不同,这个问题我们可以每次选择数字\(a[i]\)之后把对应的\(cnt[a[i]]\)赋为\(0\),这样在以后就必然不会将\(a[i]\)加入单调队列,也就不会再选择这个数字了
其次我们考虑每次向右拓展的最远位置是哪里,由于每次选择数字我们都会将一个数字的\(cnt\)值设为\(0\),而且每次选择之后,选择位置的右边剩下序列中存在的与已选择数字不同的数字种类只能减少一个,也就是减少一次的机会已经被我们选择的这个数给用掉了,那么每次我们只能向右拓展直到当前位数字的\(cnt\)值为\(0\)的前一个位置。
最后考虑如何维护待选集合里的最小值,这里我们可以用一个单调递增的单调队列来维护,队头维护的就是最小值,因此每次只需从队头取即可。
道理是这么个道理,但实现一下细节太多,把上述思路转换一下,循环\(1-m\),每次维护优先队列把大于等于\(a[i]\)的都弹掉,加入当前数字,如果当前数字\(cnt\)减为零了,就把队列里所有小于等于当前值的数以及当前值都加入答案,为什么呢?因为题目里满足\(a[i]\)中必然会出现按\(1-n\)的每一个数,也就是说我们必须要选择\(1-n\)中的每一个数,那当前值的\(cnt\)都要变为零了我们还不选,以后就没得选了,所以把队列里比当前值小的都加进入,再把当前值加入
#include<algorithm>
#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<set>
#define x first
#define y second
#define ll long long
#define pii pair <int, int>
#define mp(a, b) make_pair(a, b)
#define rep(i, a, b) for(int i = a; i <= b; ++i)
#define per(i, a, b) for(int i = a; i >= b; --i)
#define clr(a, b) memset((a),b,sizeof(a))
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 << 3) + (s << 1) + (ch ^ 48); ch = getchar(); }
return s * w;
}
const int N = 1e6 + 50;
int n, m, num, ans[N], a[N], cnt[N], vis[N];
deque <int> q;
int main() {
m = read(); n = read();
rep(i, 1, m) {
a[i] = read();
cnt[a[i]] ++;
}
rep(i, 1, m) {
if(cnt[a[i]] == 0) continue;
cnt[a[i]] --;
while(vis[a[i]] == 0 && q.size() && q.back() >= a[i]) {
vis[q.back()] = 0;
q.pop_back();
}
if(vis[a[i]] == 0) {
q.push_back(a[i]);
vis[a[i]] = 1;
}
if(cnt[a[i]] == 0) {
while(q.size() && q.front() <= a[i]) {
int x = q.front();
q.pop_front();
ans[++ num] = x;
cnt[x] = 0;
}
}
}
rep(i, 1, num) {
if(i != 1) printf(" ");
printf("%d", ans[i]);
}
return 0;
}
另一种单调栈的写法

注意每次弹出元素需要保证后面还有这个元素,若后面没有这个元素我们又把他弹出了,以后就再也没得选了,而这个题要求我们\(1-n\)的每个数都要出现一次,就不满足题目要求了
#include<algorithm>
#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<set>
#define x first
#define y second
#define ll long long
#define pii pair <int, int>
#define mp(a, b) make_pair(a, b)
#define rep(i, a, b) for(int i = a; i <= b; ++i)
#define per(i, a, b) for(int i = a; i >= b; --i)
#define clr(a, b) memset((a),b,sizeof(a))
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 << 3) + (s << 1) + (ch ^ 48); ch = getchar(); }
return s * w;
}
const int N = 1e6 + 50;
int n, k, a[N], vis[N], lst[N];
int main() {
n = read(); k = read();
rep(i, 1, n) {
a[i] = read();
lst[a[i]] = i;
}
stack <int> stk;
rep(i, 1, n) {
if(vis[a[i]]) continue;
while(stk.size() && stk.top() > a[i] && lst[stk.top()] > i) {
vis[stk.top()] = 0;
stk.pop();
}
stk.push(a[i]);
vis[a[i]] = 1;
}
vector <int> ans;
while(stk.size())
ans.push_back(stk.top()), stk.pop();
reverse(ans.begin(), ans.end());
rep(i, 0, ans.size() - 1) {
if(i) printf(" ");
printf("%d", ans[i]);
}
return 0;
}

浙公网安备 33010602011771号