题意:给一个 1 1 1到 n n n的排列。
判断能否通过以下四种操作按顺序输出 1 1 1到 n n n,并输出字典序最小的操作序列。
操作分 a , b , c , d a,b,c,d a,b,c,d,分别为将第一个元素压入、弹出栈 1 1 1,压入、弹出栈 2 2 2
元素被弹出栈时算作输出。
n ≤ 1 0 3 n \le 10^3 n≤103
n ≤ 1 0 3 n \le 10^3 n≤103,复杂度上限基本 Θ ( n 2 ) \Theta(n^2) Θ(n2)了
很显然数字可以被分为压入栈
1
1
1和栈
2
2
2的,记为黑白二色
都知道一个栈的时候,有如果
∃
i
<
j
<
k
,
a
[
k
]
<
a
[
i
]
<
a
[
j
]
∃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]
∃k>j>i,a[k]<a[i]<a[j]那么
i
,
j
i,j
i,j不能在同一个栈
给
i
,
j
i,j
i,j连上边代表
i
,
j
i,j
i,j不能同色。
判断有解黑白染色即可。字典序最小所以从
1
1
1开始染,给开始染的点染栈
1
1
1的色
然后模拟两个栈就好了。
Θ
(
n
2
)
\Theta(n^2)
Θ(n2)来自于判断
i
,
j
i,j
i,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;
}