题目背景
NOIP2008提高组试题4。
题目描述
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
解析:
就算知道是二分图染色还是不会TT。
首先考虑怎么判断是否有解。
a [ i ] 和 a [ j ] 不能压入同一个栈(在同一个栈中出现过)⇔ 存在一个k,使得 i < j < k 且 a [ k ] < a [ i ] < a [ j ]
严格证明就比较复杂了,我就说如何感性理解吧(反正在考场上也没几个人会去严格证明)。
因为一个数只能进出一次,k 要排在前面所以弹出 k 时 i 和 j 都在栈里,如果两者在同一个栈弹出后顺序就错误了,然后找不到除这种情况外的反例,于是就这样了。。。
所以如果输入数据不能使得所有限制成立则无解,然后就可以转化成二分图染色来判定,不能在同一个栈中就连边。
关于输出答案,因为要让字典序最小肯定是尽量加在S1里面,实在不行加进S2。
代码:
#include <bits/stdc++.h>
using namespace std;
const int Max=1010;
int n,m=1,size,tot1,tot2,p1[Max],p2[Max];
int first[Max],f[Max],num[Max],vis[Max];
struct shu{int to,next,len;}edge[Max*Max];
inline int get_int()
{
int x=0,f=1;
char c;
for(c=getchar();(!isdigit(c))&&(c!='-');c=getchar());
if(c=='-') f=-1,c=getchar();
for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
return x*f;
}
inline void build(int x,int y)
{
edge[++size].next=first[x],first[x]=size,edge[size].to=y;
edge[++size].next=first[y],first[y]=size,edge[size].to=x;
}
inline void pre()
{
f[n]=num[n];
for(int i=n-1;i>=1;i--) f[i]=min(f[i+1],num[i]);
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
if(f[j]<num[i]&&num[i]<num[j]) build(i,j);
}
inline bool dfs(int p,int color)
{
vis[p]=color;
for(int u=first[p];u;u=edge[u].next)
{
int to=edge[u].to;
if(!vis[to]) dfs(to,3-color);
else if(color==vis[to]) return 0;
}
return 1;
}
int main()
{
n=get_int();
for(int i=1;i<=n;i++) num[i]=get_int();
pre();
for(int i=1;i<=n;i++) if(!vis[i]&&!dfs(i,1)) {puts("0");return 0;}
for(int i=1;i<=n;i++)
{
if(vis[i]==1)p1[++tot1]=num[i],printf("a ");
else p2[++tot2]=num[i],printf("c ");
while(m==p1[tot1]||m==p2[tot2])
{
if(m==p1[tot1]) tot1--,printf("b ");
else tot2--,printf("d ");
m++;
}
}
return 0;
}