2022.3.22 程式碼源每日一題div1 #608. 字典序最小
阿新 • • 發佈:2022-03-23
首先考慮到選擇數字應該從第一個數字向後逐個確定
那麼每一個數字的可選範圍是哪些呢?
比如開始的時候,我們要選擇\(n\)個數,那我們應該保證第一個選擇之後,第一個數對應位置的右邊還有\(n-1\)個與已選數字不同的數字
用\(cnt[a[i]]\)表示每個\(a[i]\)出現的次數
首先,我們要保證每次選的數字與已選數字不同,這個問題我們可以每次選擇數字\(a[i]\)之後把對應的\(cnt[a[i]]\)賦為\(0\),這樣在以後就必然不會將\(a[i]\)加入單調佇列,也就不會再選擇這個數字了
其次我們考慮每次向右拓展的最遠位置是哪裡,由於每次選擇數字我們都會將一個數字的\(cnt\)值設為\(0\)
最後考慮如何維護待選集合裡的最小值,這裡我們可以用一個單調遞增的單調佇列來維護,隊頭維護的就是最小值,因此每次只需從隊頭取即可。
道理是這麼個道理,但實現一下細節太多,把上述思路轉換一下,迴圈\(1-m\),每次維護優先佇列把大於等於\(a[i]\)的都彈掉,加入當前數字,如果當前數字\(cnt\)減為零了,就把佇列裡所有小於等於當前值的數以及當前值都加入答案,為什麼呢?因為題目裡滿足\(a[i]\)
#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;
}