第一步:判断能否双栈排序
感性地理解,我们要让最后输出的序列是升序,显然栈里面数字都要保证是降序的;
摘自 zjp_shadow的题解
{1,2,3} 这组数据是可以放入同一个栈中的,无非是放入就立刻拿出
如果二分图染色成功,每个数字都会得到一个颜色,同样颜色的数字必须在一个栈中
第二步:输出最小字典序的排序
一个较为麻烦的模拟,需要特判很多情况
因为不同操作的顺序会影响字典序,我们应该优先完成第一个栈的入栈和出栈,再考虑第二个栈,即:栈2的任何操作前都考虑栈1能不能操作
同时很重要的一点,得出的序列要求是升序的:{1、2、3、4…},我们就必须要记录每次该弹出哪个数字,不能弹错顺序。
Code:
#include#include #include #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 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 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 & aa, int x) { aa.pop(); if (x == 1)cout << " b"; else cout << " d"; top++; } inline bool check(stack & 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; }


![洛谷:P1155 [NOIP2008 提高组] 双栈排序(二分图、模拟) 洛谷:P1155 [NOIP2008 提高组] 双栈排序(二分图、模拟)](http://www.mshxw.com/aiimages/31/738616.png)
