P1155 [NOIP 2008 提高组] 双栈排序

将一组数据分成两组,且组内不能冲突。考虑使用二分图。

我们来思考什么样的两个数不能存在于一个栈中。因为最后要求我们升序排序输出,所以在一个栈中的数字必定是降序。

那么当 \(i<j\) 时并且 \(p_i<p_j\)。\(i,j\) 便不能存在于同一个栈中吗?

显然不是,我们来看 \(P=\left [1,3,2,4\right ]\)。前两个数 \(1,3\) 是可以满足上文的情况的,但是他们可以存在在一个栈里面 。为什么会造成这种情况呢?我们发现数字 \(1\) 入栈后就可以直接弹,因为它后面没有比它小的数字了。

所以我们还要加一个附加条件。即当 \(i<j<k\) 且 \(p_k<p_i<p_j\) 满足时,\(i,j\) 不能共存于一个栈中。之后我们便在 \(i,j\) 之间建边,去做二分图即可。

之后我们考虑如何让字典序最小,观察到两个栈的操作是独立的,那么如果相邻的两个操作是一个栈插入,另一个栈弹出,那么可以交换位置,以此让字典序达到最小。

讲的很详细了,代码就不放注释了。

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
const int N=1007;
int n,a[N],cnt=1,tot;
stack<int> s1,s2;
vector<int> G[N];
char ans[N*3];
bool vis[N],col[N];
void dfs(int u,int co){
//	cout<<u<<' '<<co<<'\n';
	vis[u]=1,col[u]=co;
	for(auto v:G[u]){
		if(vis[v]) {
			if(col[v]==col[u]) {
				cout<<0;
				exit(0);
			}
			continue;
		}
		dfs(v,co^1);
	}
}
int main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=n-1,x=a[n];i>1;i--){
		for(int j=i-1;j>=1;j--){
			if(a[j]<a[i]&&x<a[j]) G[a[j]].push_back(a[i]),G[a[i]].push_back(a[j]);
		}
		x=min(x,a[i]);
	}
	for(int i=1;i<=n;i++){
		if(!vis[a[i]]) dfs(a[i],0);
	}
//	for(int i=1;i<=n;i++) cout<<col[i]<<' ';
//	cout<<'\n';
	for(int i=1;i<=n;i++){
		if(col[a[i]]==0) ans[++tot]+='a',s1.push(a[i]);
		else ans[++tot]+='c',s2.push(a[i]);
		while((!s1.empty()&&s1.top()==cnt)||(!s2.empty()&&s2.top()==cnt)){
			if(!s1.empty()&&s1.top()==cnt) ans[++tot]='b',s1.pop(),cnt++;
			else ans[++tot]='d',s2.pop(),cnt++;
		}
	}
	for(int i=tot-1;i>=1;i--){
		for(int j=i;j<tot;j++){
			if((ans[j]=='c'&&ans[j+1]=='b')||(ans[j]=='d'&&ans[j+1]=='a')) swap(ans[j],ans[j+1]);
			else break;
		}
	}
	for(int i=1;i<=tot;i++) cout<<ans[i]<<' ';
	return 0;
}