[NOIP2008 提高组] 双栈排序
\(\mathcal{O(n^3)}\) 竟然过了,认真卡可能卡得掉。
\(update:\) 已经优化成 \(\mathcal{O(n^2)}\) 的了。
题目大意
有一个长度为 \(n\) 的序列(是 \(1\sim n\) 的排列),现在有 \(2\) 个栈 \(S_1\),\(S_2\),还有 \(4\) 个操作:
-
操作 \(a\):如果输入序列不为空,将第一个元素压入栈 \(S_1\)。
-
操作 \(b\):如果栈 \(S_1\) 不为空,将 \(S_1\) 栈顶元素弹出至输出序列。
-
操作 \(c\):如果输入序列不为空,将第一个元素压入栈 \(S_2\)。
-
操作 \(d\):如果栈 \(S_2\) 不为空,将 \(S_2\) 栈顶元素弹出至输出序列。
判断是否能使得输出序列升序排序, 若不能,则输出 \(0\);若能,则输出字典序最小的操作序列。
\(1\le n\le 1000\)。
题目分析
如何判断输入序列 \(a\) 是否有解:
结论:当 \(a_k\lt a_i\lt a_j\) 且 \(i\lt j\lt k\) 时 \(i,j\) 不在同一个栈中。
证明:
反证法,如果 \(i,j,k\) 在一个栈中,因为 \(a_i\lt a_j\),所以 \(i\) 要比 \(j\) 先出栈,又因为 \(a_k\lt a_i\),所以 \(k\) 比 \(i\) 先出栈。那么离谱的事情就来了,\(k\) 要入栈的话 \(i,j\) 必须先入栈,但 \(i\lt j\),故也就会导致 \(j\) 比 \(i\) 先出栈。这与前面的“所以 \(i\) 要比 \(j\) 先出栈”相悖,所以结论成立。
故如果 \(i,j\) 不能在同一个栈中,则连边 \((i,j)\),最后可以连出一个图。我们使用染色法判断这个图是否为二分图,试过不是则无解。染色时,左部点全部标记上 \(1\),右部点全部标记上 \(2\)。
如何输出任意一组可行解:
依次将每个数压入它的编号所代表的的栈(即染色时的标记),出栈入栈时保证编号连续即可。具体地,用 \(id\) 表示当前应该输出的数字,找一找 \(id\) 在哪个栈并在对应的栈出栈。
如何保证输出的可行解字典序最小
向 \(S_2\) 入栈时,先尽可能从 \(S_1\) 出栈。总的来说,尽可能操作 \(S_1\)。
代码
// Problem: P1155 [NOIP2008 提高组] 双栈排序
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P1155
// Memory Limit: 128 MB
// Time Limit: 1000 ms
// Date:2022-06-18 15:42
//
// Powered by CP Editor (https://cpeditor.org)
#include <iostream>
#include <cstdio>
#include <climits>//need "INT_MAX","INT_MIN"
#include <cstring>//need "memset"
#include <numeric>
#include <algorithm>
#include <cmath>
#include <stack>
#define enter putchar(10)
#define debug(c,que) std::cerr << #c << " = " << c << que
#define cek(c) puts(c)
#define blow(arr,st,ed,w) for(register int i = (st);i <= (ed); ++ i) std::cout << arr[i] << w;
#define speed_up() std::ios::sync_with_stdio(false),std::cin.tie(0),std::cout.tie(0)
#define mst(a,k) memset(a,k,sizeof(a))
#define stop return(0)
const int mod = 1e9 + 7;
inline int MOD(int x) {
if(x < 0) x += mod;
return x % mod;
}
namespace Newstd {
char buf[1 << 21],*p1 = buf,*p2 = buf;
inline int getc() {
return p1 == p2 && (p2 = (p1 = buf) + fread(buf,1,1 << 21,stdin),p1 == p2) ? EOF : *p1 ++;
}
inline int read() {
int ret = 0,f = 0;char ch = getc();
while (!isdigit(ch)) {
if(ch == '-') f = 1;
ch = getc();
}
while (isdigit(ch)) {
ret = (ret << 3) + (ret << 1) + ch - 48;
ch = getc();
}
return f ? -ret : ret;
}
inline double double_read() {
long long ret = 0,w = 1,aft = 0,dot = 0,num = 0;
char ch = getc();
while (!isdigit(ch)) {
if (ch == '-') w = -1;
ch = getc();
}
while (isdigit(ch) || ch == '.') {
if (ch == '.') {
dot = 1;
} else if (dot == 0) {
ret = (ret << 3) + (ret << 1) + ch - 48;
} else {
aft = (aft << 3) + (aft << 1) + ch - '0';
num ++;
}
ch = getc();
}
return (pow(0.1,num) * aft + ret) * w;
}
inline void write(int x) {
if(x < 0) {
putchar('-');
x = -x;
}
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
}
}
using namespace Newstd;
const int INF = 0x3f3f3f3f;
const int N = 1005;
struct Graph {
int v,nxt;
} gra[N * N << 1];
int head[N],col[N],a[N],suc[N];
std::stack<int>st[3];
int n,idx,now;
inline void add(int u,int v) {
gra[++ idx] = (Graph){v,head[u]},head[u] = idx;
}
inline bool make_color(int now,int color) {
col[now] = color;
for (register int i = head[now];i;i = gra[i].nxt) {
int v = gra[i].v;
if (!col[v]) {
if (!make_color(v,3 - color)) return false;
} else if (col[v] == color) {
return false;
}
}
return true;
}
inline bool check(int x) {
return !st[x].empty() && st[x].top() == now + 1;
}
inline void out(int x) {
now ++;
st[x].pop();
if (x == 1) printf("b ");
else printf("d ");
}
int main(void) {
n = read();
for (register int i = 1;i <= n; ++ i) a[i] = read();
suc[n] = a[n];
for (register int i = n - 1;i >= 1; -- i) suc[i] = std::min(suc[i + 1],a[i]);
for (register int i = 1;i <= n; ++ i) {
for (register int j = i + 1;j < n; ++ j) {
if (suc[j + 1] < a[i] && a[i] < a[j]) {
add(i,j),add(j,i);
}
}
}
for (register int i = 1;i <= n; ++ i) {
if (col[i]) continue;
if (!make_color(i,1)) {
puts("0");
return 0;
}
}
for (register int i = 1;i <= n; ++ i) {
if (col[i] == 2) {
while (check(1)) out(1);
}
while (!st[col[i]].empty() && st[col[i]].top() < a[i]) {
if (check(col[i])) out(col[i]);
else out(3 - col[i]);
}
if (col[i] == 2) {
while (check(1)) out(1);
}
st[col[i]].push(a[i]);
if (col[i] == 1) printf("a ");
else printf("c ");
}
while (!st[1].empty()) {
if (check(1)) out(1);
else out(2);
}
while (!st[2].empty()) out(2);
return 0;
}