二分圖染色 - 雙棧排序(NOIP2008提高組)
題目描述
Tom 最近在研究一個有趣的排序問題。如圖所示,通過 2 個棧 S1 和 S2,Tom希望藉助以下 4 種操作實現將輸入序列升序排列。
操作a:
如果輸入序列不為空,將第一個元素壓入棧 S1
操作b:
如果棧 S1 不為空,將 S1 棧頂元素彈出至輸出序列
操作c:
如果輸入序列不為空,將第一個元素壓入棧 S2
操作d:
如果棧 S2 不為空,將 S2 棧頂元素彈出至輸出序列
如果一個 1~n 的排列 P 可以通過一系列操作使得輸出序列 1,2,…,(n-1),n,Tom 就稱 P 是一個“可雙棧排序排列”。例如(1,2,3,4)就是一個“可雙棧排序排列”,而(2,3,4,1)不是。下圖描述了一個將(1,3,2,4)排序的操作序列:
當然,這樣的操作序列有可能多個,對於上例(1,2,3,4),是另外一個可行的操作序列。Tom 希望知道其中字典序最小的操作序列是什麼。
輸入格式
輸入兩行:
第一行是一個整數 n 。
第二行有 n 個用空格隔開的正整數,構成一個 1~n 的排列。
輸出格式
輸出一行,如果輸入的排列不是“可雙棧排序排列”,輸出數字 0;否則輸出字典最小的操作序列,每個操作之間用空格隔開,行尾沒有空格。
樣例資料 1
輸入
4
1 3 2 4
輸出
a b a a b b a b
樣例資料 2
輸入
4
2 3 4 1
輸出
0
樣例資料 3
輸入
3
2 3 1
輸出
a c a b b d
備註
【資料範圍】
30% 的資料滿足:n<=10
50% 的資料滿足:n<=50
100% 的資料滿足:n<=1000
Analysis
就算知道是二分圖染色,也毫無思緒的我T_T
先手寫一個貪心,過了30分
然後發現這個貪心顯然錯誤,棄療。
跑去翻題解,哦哦,原來要推性質
和
(
)不能在同一個棧存在(不要求同時出現)的條件就是⇔ 存在一個k,使得 i < j < k 且 a [ k ] < a [ i ] < a [ j ]
感性理解一下:
因為一個數只能進出一次,k 要排在前面所以彈出 k 時 i 和 j 都在棧裡,如果兩者在同一個棧彈出後順序就錯誤了,然後找不到除這種情況外的反例,於是就這樣了。。。
我們把不能放在一個棧的數連一條邊,然後二分圖染色,判斷是否存在解
(我們是把二分圖的兩個點集合看做兩個棧,在同一個點集的當然不能連邊)
最後注意一下輸出,但好像沒有卡吧
Code
#include<bits/stdc++.h>
#define in read()
#define N 1009
using namespace std;
inline int read(){
char ch;int f=1,res=0;
while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
while(ch>='0'&&ch<='9'){
res=(res<<3)+(res<<1)+ch-'0';
ch=getchar();
}
return f==1?res:-res;
}
int n,s[N],mn[N],color[N];
int stk1[N<<2],tp1,stk2[N<<2],tp2;
int nxt[N*N],to[N*N],head[N],ecnt=0;
void add(int x,int y){nxt[++ecnt]=head[x];head[x]=ecnt;to[ecnt]=y;}
inline void pre_work(){
mn[n]=s[n];
for(int i=n-1;i>=1;--i) mn[i]=min(mn[i+1],s[i]);
for(int i=1;i<n;++i)
for(int j=i+1;j<=n;++j)
if(mn[j]<s[i]&&s[i]<s[j]) add(i,j),add(j,i);
}
bool dfs(int u,int col){
color[u]=col;
for(int e=head[u];e;e=nxt[e]){
int v=to[e];
if(!color[v]) dfs(v,3-col);
else if(color[v]==col) return 0;
}
return 1;
}
int main(){
n=in;
int i,j,num=1;
for(i=1;i<=n;++i) s[i]=in;
pre_work();
for(i=1;i<=n;++i) if(!color[i]&&!dfs(i,1)) return printf("0"),0;
for(i=1;i<=n;++i){
if(color[i]==1) stk1[++tp1]=s[i],printf("a ");
else stk2[++tp2]=s[i],printf("c ");
while(num==stk1[tp1]||num==stk2[tp2]){
if(num==stk1[tp1]){
tp1--;printf("b");
if(num!=n) printf(" ");
}
else {
tp2--,printf("d");
if(num!=n) printf(" ");
}
num++;
}
}
return 0;
}