算法刷题笔记(一)(1) Acwing.153. 双栈排序
Solution
https://www.acwing.com/problem/content/155/
二分图,染色,贪心 O(n^2)
性质
当且仅当i < j < k, q[k] < q[i] < q[j] 的情况下, 两个数无法放到同一栈中
证明
充分性:
假设i < j < k, q[k] < q[i] < q[j] 的情况下, 可以放到一个栈中
那么则因为 q[i] 和 q[j] 的后面均存在一个更小的q[k],因此q[i]和q[j]都不能从栈中被弹出,所以从栈底到栈顶的元素就不是单调的降序了, 即不能放到一个栈中,即出现矛盾。
必要性
即:如果无法放到同一栈中 那么这俩数一定满足上述条件
反证
如果i,j不能进入同一个栈,且不存在 i < j < k 使
a[j]>a[i]>a[k]
那么j和j之后的元素一定在i之后出栈,所以当序列遍历到j时,一定可以让i出栈,进而j就可以进栈了。即出现矛盾
证毕。
根据此性质可以找到哪些数不能在进入一个栈中,给不能进入同一个栈的数连边
利用染色法划分二分图,不是二分图输出0,是二分图根据划分的栈模拟找到答案。
保证字典序最小
操作优先按a b c d输出
注意不要按stk1 stk2去考虑字典序
代码
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <stack>
using namespace std;
const int N = 1010;
int n;
int q[N], f[N]; //f[i] i ~ n 之间的最小值
int color[N];
bool g[N][N];
bool dfs(int u, int c)
{
color[u] = c;
for (int i = 1; i <= n; i ++ )
if (g[u][i])
{
if (color[i] == c) return false;
if (color[i] == -1 && !dfs(i, !c)) return false; //
}
return true;
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i ++ ) scanf("%d", &q[i]);
f[n + 1] = n + 1;
memset(g, false, sizeof g);
for (int i = n; i; i -- ) f[i] = min(f[i + 1], q[i]);
for (int i = 1; i <= n; i ++ )
for (int j = i + 1; j <= n; j ++ )
if (f[j + 1] < q[i] && q[i] < q[j])
g[i][j] = g[j][i] = true;
memset(color, -1, sizeof color);
bool flag = true;
for (int i = 1; i <= n; i ++ )
if (color[i] == -1 && !dfs(i, 0))
{
flag = false;
break;
}
if (!flag)
{
printf("0\n");
return 0;
}
stack<int> stk1, stk2;
int now = 1;
int j = 1;
for(int i = 1; i <= 2 * n; i++)
{
if(j <= n && !color[j] && (!stk1.size() || stk1.size() && stk1.top() > q[j]))
{
stk1.push(q[j]);
j ++ ;
cout << "a ";
}
else if(stk1.size() && stk1.top() == now)
{
stk1.pop();
cout << "b ";
now ++ ;
}
else if(j <= n && color[j] && (!stk2.size() || stk2.size() && stk2.top() > q[j]))
{
stk2.push(q[j]);
j ++ ;
cout << "c ";
}
else if(stk2.size() && stk2.top() == now)
{
stk2.pop();
cout << "d ";
now ++ ;
}
}
return 0;
}

浙公网安备 33010602011771号