洛谷:P1155 [NOIP2008 提高组] 双栈排序(二分图、模拟)

双城之栈

在这里插入图片描述

  • 第一步:判断能否双栈排序

感性地理解,我们要让最后输出的序列是升序,显然栈里面数字都要保证是降序的;

摘自 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;
}
/*
*/
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值