1. 程式人生 > >[Luogu1155] [NOIP2008] 雙棧排序 [黑白染色]

[Luogu1155] [NOIP2008] 雙棧排序 [黑白染色]

題意:給一個11nn的排列。 判斷能否通過以下四種操作按順序輸出11nn,並輸出字典序最小的操作序列。 操作分a,b,c,da,b,c,d,分別為將第一個元素壓入、彈出棧11,壓入、彈出棧22 元素被彈出棧時算作輸出。 n103n \le 10^3

n103n \le 10^3,複雜度上限基本Θ(n2)\Theta(n^2)

很顯然數字可以被分為壓入棧11和棧22的,記為黑白二色 都知道一個棧的時候,有如果 i<j<k,a[k]<a[i]<a[j]∃i<j<k,a[k]<a[i]<a[j]

那麼無解 可以轉化為如果k>j>i,a[k]<a[i]<a[j]∃k>j>i,a[k]<a[i]<a[j]那麼i,ji,j不能在同一個棧 給i,ji,j連上邊代表i,ji,j不能同色。 判斷有解黑白染色即可。字典序最小所以從11開始染,給開始染的點染棧11的色 然後模擬兩個棧就好了。 Θ(n2)\Theta(n^2)來自於判斷i
,ji,j
不能同色

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<cctype>
#include<ctime>
#include<stack>
using namespace std;
#define add_edge(a,b) nxt[++tot]=head[a],head[a]=tot,to[tot]=b
int n,ai[1005]={},
col[1005]={},mn[1005]={},tot=0; int head[1005]={},nxt[1000005]={},to[1000005]={}; void dfs(int x,int fa) { for(int i=head[x];i;i=nxt[i]) { if(to[i]==fa)continue; if(!col[to[i]])col[to[i]]=col[x]^3,dfs(to[i],x); if(col[to[i]]==col[x]) { printf("0"); exit(0); } } } stack<int>sa,sb; int main() { scanf("%d",&n); for(int i=1;i<=n;++i)scanf("%d",&ai[i]); mn[n]=ai[n]; for(int i=n-1;i;--i)mn[i]=min(mn[i+1],ai[i]); for(int i=1;i<n-1;++i) { for(int j=i+1;j<n;++j) { if(mn[j+1]<ai[i]&&ai[i]<ai[j])add_edge(i,j),add_edge(j,i); } } for(int i=1;i<=n;++i)if(!col[i])col[i]=1,dfs(i,0); int cnt=1; for(int i=1;i<=n;++i) { if(col[i]==1)sa.push(ai[i]),printf("a "); else sb.push(ai[i]),printf("c "); while((!sa.empty()&&sa.top()==cnt)||(!sb.empty()&&sb.top()==cnt)) { if(!sa.empty()&&sa.top()==cnt)sa.pop(),printf("b "); else sb.pop(),printf("d "); ++cnt; } } return 0; }