【题目描述】

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,3,2,4)就是一个“可双栈排序序列”,而(2,3,4,1)不是。下图描述了一个将(1,3,2,4)排序的操作序列:

当然,这样的操作序列有可能有几个,对于上例(1,3,2,4),是另外一个可行的操作序列。Tom希望知道其中字典序最小的操作序列是什么。

【输入】

输入文件twostack.in的第一行是一个整数n。

第二行有n个用空格隔开的正整数,构成一个1~n的排列。

【输出】

输出文件twostack.out共一行,如果输入的排列不是“可双栈排序排列”,输出数字0;否则输出字典序最小的操作序列,每两个操作之间用空格隔开,行尾没有空格。

样例输入 样例输出
4 1 3 2 4 a b a a b b a b
4 2 3 4 1 0
3 2 3 1 a c a b b d

【数据范围限制】

30%的数据满足: n<=10

50%的数据满足: n<=50

100%的数据满足: n<=1000

【题解】

这题有一点难度。

比赛时,我一看到这道题目,就想到了一种很WS的东西:暴力模拟!

然后就只拿了 27.3

其实这道题目要用到一种神奇的东西—— 二分图染色

大体的思路就是先处理好每一个数要进入的栈,再暴力模拟一遍就好了。

设 s[i] 表示输入的第i个数

设 f[i] 表示从第i个数起到第n个数的 最小值

我们会发现,总有一些数字是不能在同一个栈里的(由始至终都不可以哦~)。

如果 i < j < k,且 s[k] < s[i] < s[j],那么s[i]和s[j]就一定不能出现在同一个栈里面。

论证方法就不写了。

但每次都枚举i,j,k,明显会时超。

所以我们就可以用到 f 数组了!

认真地再看一看这条语句:

如果 i < j < k,且 s[k] < s[i] < s[j],那么s[i]和s[j]就一定不能出现在同一个栈里面。

也就是说,如果 \(\space j+1 到 n之间的最小值\space\space <s[i]<s[j]\),i,j就不能同时出现在同一个栈中。

也就是$当 f[j+1] < s[i]<s[j]( i< j ) $时,i,j就不能同时出现在同一个栈中。

这样连边就能只用一个双重循环来做了。

再设 c[i] 表示第i个数要进去的栈(栈S1用1表示,栈S2用2表示,初始化为0)

我们就开始枚举1~N的所有数字i,只要c[i]为0,就把c[i]赋值(染色)为1(这样一定最优),然后开始枚举与 i 有连边的数 j,把c[j]赋值为和c[i]的值相反的数(即当c[i]=1,c[j]=2;c[i]=2,c[j]=1),并从j那里出发,继续染与j有连边的数……如果碰到c[j]已经被赋(染)过值了,并且c[i]=c[j],那肯定是无法排序的了,输出0并结束程序就好了。

染色用DFS来做。

最后根据c[i]的值,暴力模拟一遍就可以了。

下面来说一下一些细节:

  1. 连边时一定要连 双向边,譬如要在 x,y 之间连一条边,都要连两条单向的边 x,y 和 y,x,存边最好用 邻接表
  2. 连完边后,要把1到n的数都走一次 递归 ,并把与这个数相连的所有数染完色后,从这些数继续递归染色;
  3. 每个数都只递归一次,因为重复地递归同一个数,是浪费时间而没有半点用处的;
  4. 有一种特殊的情况,就是当栈S2的栈顶可以弹栈,而S1又可以从输入的栈加入一个数时,一定要先读入,后弹栈——因为如果先弹栈,后输入,那输出队列里存的就是 d a 了,这样明显不如 a d 那么优。

建议还不太明白的同学来模拟一些这个数据:

14

2 3 1 4 14 6 12 5 11 13 7 8 9 10

还有不懂的同学可以来找我。

下面附上标程,可以参考一下。

#include<cstdio>
#include<cstdlib>
using namespace std;
#define MAX 0x7fffffff
int s[1010],f[1010],c[1010],a[1010],b[1010],edge[1010][1001];
char st[5010];bool bk[1010];
void inc(int x,int y)//建边(一定要是双向的),用领接表储存
{
edge[x][++edge[x][0]]=y;
edge[y][++edge[y][0]]=x;
}
void build(int k)//二分图染色
{
if(bk[k]) return;
bk[k]=true;
if(c[k]==0) c[k]=1;
int i,j;
for(i=1;i<=edge[k][0];i++)
{
j=edge[k][i];
if(c[k]==c[j])
{
puts("0");
exit(0);
}
if(c[k]==1) c[j]=2;
else c[j]=1;
build(j);
}
}
int main()
{
freopen("twostack.in","r",stdin);
freopen("twostack.out","w",stdout);
int n,i,j,k,ans=0,next=1;
scanf("%d",&n);
f[n+1]=MAX;
for(i=1;i<=n;i++) scanf("%d",&s[i]);
for(i=n;i>0;i--)
{
if(s[i]>f[i+1]) f[i]=f[i+1];
else f[i]=s[i];
}
for(i=1;i<n;i++)
{
for(j=i+1;j<n;j++)
{
if(s[i]<s[j]&&s[i]>f[j+1]) inc(i,j);
}
}
for(i=1;i<=n;i++) build(i);//染色
for(i=1;i<=n;i++)//暴力模拟
{
if(c[i]==1)
{
st[++ans]='a';
++a[0];
a[a[0]]=s[i];
}
else
{
st[++ans]='c';++b[0];
b[b[0]]=s[i];
}
while(a[a[0]]==next)
{
st[++ans]='b';
next++;a[0]--;
}
while(b[b[0]]==next)
{
if(i<n&&(a[0]==0||(c[i+1]==1&&a[a[0]]>s[i+1])))
{
i++;++a[0];
a[a[0]]=s[i];
st[++ans]='a';
}
st[++ans]='d';
next++;b[0]--;
}
while(a[a[0]]==next)
{
st[++ans]='b';
next++;a[0]--;
}
}
while(a[0]>0||b[0]>0)
{
while(a[a[0]]==next)
{
st[++ans]='b';
next++,a[0]--;
}
while(b[b[0]]==next)
{
st[++ans]='d';
next++,b[0]--;
}
}
for(i=1;i<ans;i++) printf("%c ",st[i]);
printf("%c\n",st[ans]);
return 0;
}

【提高组NOIP2008】双栈排序 (twostack.pas/c/cpp)的更多相关文章

  1. Vijos1605 NOIP2008 提高组T4 双栈排序 BFS

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - Vijos1605 题意概括 有1个1~n的排列,有2个栈,现在通过以下操作,使得出栈序列有序. 操作a 当前 ...

  2. Luogu1155 NOIP2008 双栈排序 【二分图染色】【模拟】

    Luogu1155 NOIP2008 双栈排序 题目描述 Tom最近在研究一个有趣的排序问题.如图所示,通过 2个栈 S1 和 S2 ,Tom希望借助以下 44 种操作实现将输入序列升序排序. 操作 ...

  3. NOIP2008双栈排序[二分图染色|栈|DP]

    题目描述 Tom最近在研究一个有趣的排序问题.如图所示,通过2个栈S1和S2,Tom希望借助以下4种操作实现将输入序列升序排序. 操作a 如果输入序列不为空,将第一个元素压入栈S1 操作b 如果栈S1 ...

  4. [NOIP2008]双栈排序 【二分图 + 模拟】

    题目描述 Tom最近在研究一个有趣的排序问题.如图所示,通过2个栈S1和S2,Tom希望借助以下4种操作实现将输入序列升序排序. 操作a 如果输入序列不为空,将第一个元素压入栈S1 操作b 如果栈S1 ...

  5. noip2008 双栈排序

    题目描述 Description \(Tom\)最近在研究一个有趣的排序问题.如图所示,通过\(2\)个栈\(S_1\)和\(S_2\),\(Tom\)希望借助以下\(4\)种操作实现将输入序列升序排 ...

  6. Noip2008双栈排序

    [问题描述] 用两个栈使一个1...n的排列变得有序.一共有四个操作: A.stack1.push() 读入一个放入栈一 B.stack1.pop() 弹出栈一放入输出序列 C.stack2.push ...

  7. NOIP2008双栈排序(贪心)

    题目描述 Tom最近在研究一个有趣的排序问题.如图所示,通过2个栈S1和S2,Tom希望借助以下4种操作实现将输入序列升序排序. 操作a 如果输入序列不为空,将第一个元素压入栈S1 操作b 如果栈S1 ...

  8. [题解] [NOIP2008] 双栈排序——关系的冲突至图论解法

    Problem 题目描述 Tom最近在研究一个有趣的排序问题.如图所示,通过2个栈S1和S2,Tom希望借助以下4种操作实现将输入序列升序排序. 操作a 如果输入序列不为空,将第一个元素压入栈S1 操 ...

  9. [luogu1155 NOIP2008] 双栈排序 (二分图染色)

    传送门 Description Input 第一行是一个整数 n . 第二行有 n 个用空格隔开的正整数,构成一个 1−n 的排列. Output 共一行,如果输入的排列不是"可双栈排序排列 ...

随机推荐

  1. Markdown 标记语言指北

    这是班刊约稿的一篇文章. 全文约6000字, 预计需要 60 分钟读完. Markdown 标记语言指北 TOC 什么是 Markdown? Markdown 可以用来干什么? 第一步? 一些专业一点 ...

  2. 冒泡排序和选择排序(bubble sort and selection sort)

    Bubble sort Basic Method: import random nums = [random.randint(1,20) for _ in range(10)] #制作一个无序数字列表 ...

  3. POJ 1995(有关快速幂运算的一道水题)

    Raising Modulo Numbers Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 9745   Accepted: ...

  4. POJ 3692 幼儿园做游戏 最大团 模板题

    Kindergarten Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 6191   Accepted: 3052 Desc ...

  5. HDU6579 Operation

    题目链接 问题分析 区间求异或和最大,比较自然的想到了线性基.而每次求一个区间的线性基显然是行不通的.我们考虑在每个位置求出首位置到当前位置的线性基.同时我们要使线性基中高位的位置所选的数尽量靠后.这 ...

  6. 货郎担问题(TSP问题)

    货郎担问题也叫旅行商问题,即TSP问题(Traveling Salesman Problem),是数学领域中著名问题之一. 有n个城市,用1,2,…,n表示,城i,j之间的距离为dij,有一个货郎从城 ...

  7. JavaWeb_(session和application)用户登录注册模板_进阶版

    用户登录注册模板_基础版 传送门 用户登录注册模板进阶版 添加了获得获得当前登录用户信息及存储登录人数 用户登录后,在首页.注册页.登录页显示登录用户信息和存储登录人数信息 目录结构 <%@pa ...

  8. [CSP-S模拟测试]:点亮(状压DP+树上背包DP)

    题目传送门(内部题121) 输入格式 第一行,一个正整数$n$. 第二行,$n-1$个正整数$p_2,p_3,...,p_n$.保证$p_u$是在$1$到$u-1$中等概率随机选取的. 接下来$n$行 ...

  9. plsql developer连接数据库时出现ORA-01033错误的解决方法

    1.首先以管理员的身份登录本地数据库:sqlplus "/as sysdba"如下图:        2.卸载数据: shutdown normal          3. 重新装 ...

  10. 多个swiper使用样式出了问题

    observer:true,//修改swiper自己或子元素时,自动初始化swiper observeParents:true,//修改swiper的父元素时,自动初始化swiper 不行直接设  w ...