双栈排序——二分图染色的运用典型
让我们考虑,如果告诉每个数如果进入哪个栈,那么通过模拟就能确定序列
解释:当每个元素进哪个栈确定时,不确定的只有某个元素进一个栈,和另一个栈弹出元素的顺序。
这种情况,因为要求字典序最小,而
这样,我们知道,只要确定每个数进入哪个栈,问题就解决了!
怎么确定呢??
也就是确定哪些元素无论是否同时在栈内,都不能进入同一个栈。即,找到所有
如果有一个顺序靠前的元素,和一个顺序靠后的元素,前者大于后者,前者和后者可以进入同一个栈,即前面的元素进入没有弹出时,后面的元素进入。
如果有一个顺序靠前的元素
提醒:这里
1.前者和后者可以进入同一个栈,即前面的元素
2.什么时候前提不成立?在
通过后缀最小值处理这个复杂度在
谁和谁矛盾判断出来了,那么矛盾的每一对进行建边,跑二分图染色,如果能成功染色,就是一个可行方案。
#include<bits/stdc++.h>
using namespace std;
int n;
const int N=1010;
vector<int>G[N];
int a[N],sa[N];
int col[N];//1 进a 2 进b
vector<int>stuc[3];//栈。手写或者用 stack 也行,不过我没用习惯。
bool dfs(int x,int c)
{
col[x]=c;
for(int i=0;i<G[x].size();i++)
{
int j=G[x][i];
if(col[j]==c)return 0;
if(col[j]==0)
if(!dfs(j,c%2+1))return 0;
}
return 1;
}
inline void solve(){
cin>>n;
sa[n+1]=114514;//任意大于 1000 的数
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=n;i>=1;i--)
sa[i]=min(sa[i+1],a[i]);
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
if(a[i]<a[j]&&sa[j]<a[i])
G[i].push_back(j),G[j].push_back(i);
for(int i=1;i<=n;i++)
if((!col[i])&&(!dfs(i,1))){cout<<0<<endl;return;}
//给没染过的每个点跑染色
//下面,进行模拟
int s=1;//辅助变量,表示已经弹出了值为 [1,s) 的数,即该弹 s 了
for(int i=1;i<=n;i++)
{
int t=col[i];
while(stuc[t].size()&&*(stuc[t].end()-1)<a[i])//*(stuc[t].end()-1) 表示取尾元素的值
if(*(stuc[1].end()-1)==s)
stuc[1].pop_back(),cout<<"b ",s++;
else stuc[2].pop_back(),cout<<"d ",s++;
if(t==2)
while(stuc[1].size()&&*(stuc[1].end()-1)==s)
stuc[1].pop_back(),cout<<"b ",s++;
stuc[t].push_back(a[i]),
cout<<(t==1?"a ":"c ");
}
while(s<=n)
{
while(stuc[1].size()&&*(stuc[1].end()-1)==s)
stuc[1].pop_back(),cout<<"b ",s++;
while(stuc[2].size()&&*(stuc[2].end()-1)==s)
stuc[2].pop_back(),cout<<"d ",s++;
}
}
int main()
{
solve();
const int _=0;
return ~~(0^_^0);
}