1. 程式人生 > >洛谷1155 雙棧排序(二分圖染色)

洛谷1155 雙棧排序(二分圖染色)

(因為洛谷的格式問題所以不貼題目描述了)

【題目分析】

首先我們發現,對於所有輸出的順序,我們可以視作一個佇列,所以b、d操作就可視作將stack1、stack2的棧頂元素彈入que的隊尾,如果入隊順序可以為1-n,那麼就可行,否則不行。

然後考慮順序,這裡結合二分圖(非匹配)的思想,將1~n視作n個點,現在就要對這n個點進行黑白染色(分別代表兩個集合),現在需要思考的就是如何將所有點分在兩個集合中。

首先判斷是否有解,考慮對於任意兩個數a[i]和a[j]來說,它們不能壓入同一個棧中的充要條件是什麼(注意沒有必要使它們同時存在於同一個棧中,只是壓入了同一個棧)。實際上,這個條件p是:存在一個k,使得i<j<k且a[k]<a[i]<a[j]。

有了這個條件我們就可以進行染色了,如果存在一個情況是i<j<k且a[k]<a[i]<a[j],那麼i和j肯定沒法在同一個棧中,所以我們將i和j連邊(注意i到j要連一條,同樣j到i也要連一條)

考慮了有解之後就是尋找字典序最小的解,因為壓入stack1的操作為a,所以我們希望編號小的節點優先壓入stack1中,然後我們發現對於這樣一個圖,不同強連通分量之間的顏色是互不影響的,所以我們找到每一個強連通分量中編號最小的點將其壓入stack1,染色為1,然後dfs一遍即可。

最後一點小優化:我們肯定不可能去列舉所有的i<j<k且a[k]<a[i]<a[j],因為這樣複雜度會上升到O(n^3),所以我們用一個數組b記錄預處理出的所有a[i]到a[n]的最小值,然後掃一遍看看是否滿足b[i]<a[i]且a[i]<a[j]。

【程式碼~】

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e3+10;
const int MAXM=2e6+10;

int n,m,cnt;
int head[MAXN],col[MAXN];
int nxt[MAXM],to[MAXM];
int a[MAXN],b[MAXN],c[MAXN];
int stk1[MAXN],stk2[MAXN],top1,top2;
int minn[MAXN];

void add(int x,int y)
{
	cnt++;
	nxt[cnt]=head[x];
	head[x]=cnt;
	to[cnt]=y;
}


void dfs(int u)
{
	for(int i=head[u];i!=-1;i=nxt[i])
	{
		int v=to[i];
		if(!c[v])
		{
			c[v]=3-c[u];
			dfs(v);
		}
		if(c[u]==c[v])
		{
			cout<<0;
			exit(0);
		}
	}
}

int main()
{
	memset(head,-1,sizeof(head));
	scanf("%d",&n);
	for(int i=1;i<=n;++i)
	  scanf("%d",&a[i]);
	minn[n]=a[n];
	for(int i=n-1;i>=1;--i)
	  minn[i]=min(minn[i+1],a[i]);
	for(int i=1;i<n-1;++i)
	  for(int j=i+1;j<n;++j)
	    if(a[i]<a[j]&&minn[j+1]<min(a[i],a[j]))
		  add(i,j),add(j,i);
	for(int i=1;i<=n;++i)
	  if(!c[i])
	  {
	  	  c[i]=1;
	      dfs(i);
	  }
	int now=1;
	for(int i=1;i<=n;++i)
	{
		if(c[i]==1)
		{
		    stk1[++top1]=a[i];
		    cout<<"a ";
		}
		else
		{
			stk2[++top2]=a[i];
			cout<<"c ";
		}
		while(stk1[top1]==now||stk2[top2]==now)
		{
			if(stk1[top1]==now)
			{
				now++;
				top1--;
				cout<<"b ";
			}
			if(stk2[top2]==now)
			{
				now++;
				top2--;
				cout<<"d ";
			}
		}
	}
	return 0;
}