1. 程式人生 > >二分圖染色 - 雙棧排序(NOIP2008提高組)

二分圖染色 - 雙棧排序(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分
然後發現這個貪心顯然錯誤,棄療。

跑去翻題解,哦哦,原來要推性質
s [ i ]

s[i] s [ j ] s[j] i &lt; j i&lt;j )不能在同一個棧存在(不要求同時出現)的條件就是⇔ 存在一個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;
}