P1155 [NOIP2008 提高组] 双栈排序 (二分图+构造)
有思维的二分图染色题。
对于“双”类的题目,我们通常分开考虑单个时的性质。对于一个栈,有一个基本的定理:
若出现 \(i< j<k\),有 \(a_k<a_i<a_j\),那么一定不合法,即没有合法的出栈顺序使之有序。
对于两个栈,我们相当于把序列分成两部分,使每部分之间不矛盾。
那么我们就可以预处理出 \(f_i=\min\limits_{j=i}^ja_j\),枚举 \(i\),\(j\),若有 \(f_{j+1}<a_i<a_j\),则 \(i\) 与 \(j\) 就不能出现在一个栈中。
这种形式就是我们熟悉的二分图染色问题,两两连边的两点不能染成同一颜色。所以可以解决无解情况:无法染色即无解。
染色完之后,接下来就要考虑如何使操作序列字典序最小。
对于每一次入栈,我们都必须保证当前栈的单调性,即从栈底到栈顶递减,否则无法有序。所以在入栈前,我们先把该弹出的弹出,维护单调性。
而如果要入第二个栈,我们要先检查第一个栈是否能弹出,让字典序最小。
总结:对于模型的理解和熟悉,能够第一时间反应;贪心的完整,思路清晰。
#include <bits/stdc++.h>
typedef long long ll;
int read() {
int x = 0, f = 1;
char c = getchar();
while(!isdigit(c)) {
if(c == '-') f = -1;
c = getchar();
}
while(isdigit(c)) {
x = (x << 3) + (x << 1) + (c - '0');
c = getchar();
}
return x * f;
}
int n, cnt, pos = 1;
int col[1010], h[1010], f[1010];
int a[1010];
struct node {
int to, nxt;
} e[2000010];
void add(int u, int v) {
e[++cnt].to = v;
e[cnt].nxt = h[u];
h[u] = cnt;
}
bool dfs(int u, int fa, int co) {
col[u] = co;
for(int i = h[u]; i; i = e[i].nxt) {
int v = e[i].to;
if(v == fa) continue;
if(col[v] == -1) {
if(!dfs(v, u, co ^ 1)) return 0;
}
else if(col[u] == col[v]) return 0;
}
return 1;
}
std::stack<int> st[2];
bool pop(int c) {
if(!st[c].empty() && st[c].top() == pos) {
pos++;
st[c].pop();
if(c == 0) std::cout << "b ";
else std::cout << "d ";
return 1;
}
return 0;
}
void Solve() {
n = read();
for(int i = 1; i <= n; i++) {
a[i] = read();
}
f[n + 1] = 0x3f3f3f3f;
for(int i = n; i >= 1; i--) {
f[i] = std::min(f[i + 1], a[i]);
}
for(int i = 1; i <= n; i++) {
for(int j = i + 1; j <= n; j++) {
if(f[j + 1] < a[i] && a[i] < a[j]) {
add(i, j), add(j, i), col[i] = col[j] = -1;
}
}
}
for(int i = 1; i <= n; i++) {
if(col[i] == -1) {
if(!dfs(i, 0, 0)) {
std::cout << "0\n";
return;
}
}
}
// for(int i = 1; i <= n; i++) {
// std::cout << col[i] << "\n";
// }
for(int i = 1; i <= n; i++) {
if(col[i] == 1) {
while(pop(0));
}
while(!st[col[i]].empty() && st[col[i]].top() < a[i]) {
if(!pop(col[i])) pop(col[i] ^ 1);
}
if(col[i] == 1) {
while(pop(0));
}
st[col[i]].push(a[i]);
if(col[i] == 0) std::cout << "a ";
else std::cout << "c ";
}
while(pop(0) || pop(1)) {
if(!pop(0)) pop(1);
}
return;
}
int main() {
Solve();
return 0;
}