[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;
}
posted @ 2022-06-18 17:09  Coros_Trusds  阅读(88)  评论(0编辑  收藏  举报