双城之栈
- 第一步:判断能否双栈排序
感性地理解,我们要让最后输出的序列是升序,显然栈里面数字都要保证是降序的;
摘自 zjp_shadow的题解
{1,2,3} 这组数据是可以放入同一个栈中的,无非是放入就立刻拿出
如果二分图染色成功,每个数字都会得到一个颜色,同样颜色的数字必须在一个栈中
- 第二步:输出最小字典序的排序
一个较为麻烦的模拟,需要特判很多情况
因为不同操作的顺序会影响字典序,我们应该优先完成第一个栈的入栈和出栈,再考虑第二个栈,即:栈2的任何操作前都考虑栈1能不能操作
同时很重要的一点,得出的序列要求是升序的:{1、2、3、4…},我们就必须要记录每次该弹出哪个数字,不能弹错顺序。
Code:
#include<bits/stdc++.h>
#include<unordered_set>
#include<unordered_map>
#define mem(a,b) memset(a,b,sizeof a)
#define cinios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
#define sca scanf
#define pri printf
#define ul (u << 1)
#define ur (u << 1 | 1)
#define forr(a,b,c) for(int a=b;a<=c;a++)
#define rfor(a,b,c) for(int a=b;a>=c;a--)
//[博客地址]:https://blog.csdn.net/weixin_51797626?t=1
using namespace std;
inline int read() { int x = 0, f = 1, ch = getchar(); while (ch < '0' || ch>'9') { if (ch == '-') f = -1; ch = getchar(); } while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); } return x * f; }
inline void write(int x) { if (x < 0) putchar('-'), x = -x; if (x >= 10) write(x / 10); putchar(x % 10 + '0'); }
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> PII;
const int N = 1010, M = 400010, MM = N;
int INF = 0x3f3f3f3f, mod = 998244353;
ll LNF = 0x3f3f3f3f3f3f3f3f;
int n, m, k, T, S, D;
int g[N][N], qmin[N];
int a[N], color[N], top = 1;
stack<int> aa, cc;
bool st[N];
bool col(int x, int c) {
color[x] = c;
for (int j = 1; j <= n; j++)
if (g[x][j]) {
if (!color[j]) {
if (!col(j, 3 - c))return false;
}
else if (c == color[j])return false;
}
return true;
}
inline void popp(stack<int>& aa, int x) {
aa.pop();
if (x == 1)cout << " b";
else cout << " d";
top++;
}
inline bool check(stack<int>& aa) {
return aa.size() && aa.top() == top;//判断栈顶是否为当前该弹的数字
}
void solve() {
cin >> n;
forr(i, 1, n)cin >> a[i];
qmin[n + 1] = n + 1;
rfor(i, n, 1)qmin[i] = min(a[i], qmin[i + 1]);//预处理一个后缀min
forr(i, 1, n - 2)
forr(j, i + 1, n - 1)
if (a[i] < a[j] && qmin[j + 1] < a[i])//当且仅当此情况
g[i][j] = g[j][i] = 1;//我们判断i、j不能同色
forr(i, 1, n)
if (!color[i]) {
if (!col(i, 1)) { //二分图染色
cout << 0;
return;
}
}
top = 1;
bool f = false;
forr(i, 1, n) {
if (color[i] == 1) {
while (aa.size() && aa.top() < a[i]) {
//若当前栈中有比要塞入的数(a[i])小的数
//显然要弹到 a[i] 为止
//但不一定这些数都在栈1中,所以要判断弹栈1还是栈2,能弹栈1就弹栈1
if (check(aa))popp(aa, 1);
else popp(cc, 2);
}
aa.push(a[i]);
if (!f)f = true, cout << "a";
else cout << " a";
}
else {
//重点:
//弹栈2之前时,我们优先把栈1能弹都弹了,这样能保证字典序最小
while (check(aa))popp(aa, 1);
while (cc.size() && cc.top() < a[i]) {
if (check(cc))popp(cc, 2);
else popp(aa, 1);
}
while (check(aa))popp(aa, 1);//再此之后也是
//反正栈2的任何操作前都要先做栈1的操作!
cc.push(a[i]);
if (!f)f = true, cout << "c";
else cout << " c";
}
}
//最后弹完所有
while (aa.size()) {
if (check(aa))popp(aa, 1);
else popp(cc, 2);
}
while (cc.size())popp(cc, 2);
}
int main() {
cinios;
solve();
return 0;
}
/*
*/