您的位置:首页 > 其它

[栈 二分图染色] NOIP2008 双栈排序

2017-10-16 15:24 393 查看
一道远古时期的联赛题,比较经典。

由于联赛快到了,这里就写一写有理有据的思考过程:

首先,这种题目应该手玩几次。我们想让字典序最小,肯定尽量往 S1 放。而什么时候需要用第二个栈呢?所以我们考虑当只有一个栈时,什么情况下不能排序。自己试一试之后,发现只有这种情况:

存在 i<j<k, ak<ai<aj, 。这种情况下,因为后面有 k 要先出来,导致 j 不能在 i 之后出栈。可以发现没有其他情况会导致无解。

所以这样的i, j 一定不能放在同一栈中。

所以我们可以 O(n2) 得到所有约束。01 染色之后就能判无解,并得到两个需要放在不同栈的集合。剩余的其他点就放到 S1 。

知道放哪个栈就很简单了,模拟即可,字符小的操作优先。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int maxn=1005,maxe=maxn*maxn;
int n,a[maxn],_min[maxn],c[maxn];
int fir[maxn],nxt[maxe],son[maxe],tot;
bool vis[maxn];
void add(int x,int y){
son[++tot]=y; nxt[tot]=fir[x]; fir[x]=tot;
}
void dfs(int x){
for(int j=fir[x];j;j=nxt[j])
if(c[son[j]]==-1) c[son[j]]=c[x]^1, dfs(son[j]);
else if(c[son[j]]==c[x]) printf("0\n"),exit(0);
}
int stk1[maxn],stk2[maxn],top1,top2;
int main(){
freopen("hh.in","r",stdin);
freopen("hh.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
_min[n+1]=1e9; for(int i=n;i>=1;i--) _min[i]=min(_min[i+1],a[i]);
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
if(a[i]<a[j]&&a[i]>_min[j+1]) add(i,j), add(j,i), vis[i]=vis[j]=true;
memset(c,255,sizeof(c));
for(int i=1;i<=n;i++) if(vis[i]){
if(c[i]==-1) c[i]=0, dfs(i);
} else c[i]=0;
int top_out=1,top_in=1;
while(top_out<=n){
if(stk1[top1]==top_out) printf("b "), top1--, top_out++; else
if(c[top_in]==0) printf("a "), stk1[++top1]=a[top_in++]; else
if(stk2[top2]==top_out) printf("d "), top2--, top_out++; else
if(c[top_in]==1) printf("c "), stk2[++top2]=a[top_in++];
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: