NOIP2008 双栈队列
1. 双栈排序
(twostack.pas/c/cpp)
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)排序的操作序列:<a,c,c,b,a,d,d,b>
当然,这样的操作序列有可能有几个,对于上例(1,3,2,4),<a,c,c,b,a,d,d,b>是另外一个可行的操作序列。Tom 希望知道其中字典序最小的操作序列是什么。
【输入】
输入文件 twostack.in
的第一行是一个整数 n。
第二行有 n
个用空格隔开的正整数,构成一个 1~n
的排列。
【输出】
输出文件 twostack.out
共一行,如果输入的排列不是“可双栈排序排列”,输出数字
0;
否则输出字典序最小的操作序列,每两个操作之间用空格隔开,行尾没有空格。
【输入输出样例1】
|
twostack.in |
twostack.out |
|
4 1 3 2 4 |
a b a a b b a b |
【输入输出样例2】
|
twostack.in |
twostack.out |
|
4 2 3 4 1 |
0 |
【输入输出样例3】
|
twostack.in |
twostack.out |
|
3 2 3 1 |
a c a b b d |
【限制】
30%的数据满足:
n<=10
50%的数据满足:
n<=50
100%的数据满足:
n<=1000
【思路】
构造“不可能”边+二分图着色
点的分配转化为了二分图的着色。
(以下摘自洛谷OI【题解】)
考虑对于任意两个数q1[i]和q1[j]来说,它们不能压入同一个栈中的充要条件是什么(注意没有必要使它们同时存在于同一个栈中,只是压入了同一个 栈).实际上,这个条件p是:存在一个k,使得i<j<k且q1[k]<q1[i]<q1[j].
首先证明充分性,即如果满足条件p,那么这两个数一定不能压入同一个栈.这个结论很显然,使用反证法可证.
假设这两个数压入了同一个栈,那么在压入q1[k]的时候栈内情况如下:
…q1[i]…q1[j]…
因为q1[k]比q1[i]和q1[j]都小,所以很显然,当q1[k]没有被弹出的时候,另外两个数也都不能被弹出(否则q2中的数字顺序就不是1,2,3,…,n了).
而之后,无论其它的数字在什么时候被弹出,q1[j]总是会在q1[i]之前弹出.而q1[j]>q1[i],这显然是不正确的.
接下来证明必要性.也就是,如果两个数不可以压入同一个栈,那么它们一定满足条件p.这里我们来证明它的逆否命题,也就是"如果不满足条件p,那么这两个数一定可以压入同一个栈."
不满足条件p有两种情况:一种是对于任意i<j<k且q1[i]<q1[j],q1[k]>q1[i];另一种是对于任意i<j,q1[i]>q1[j].
第一种情况下,很显然,在q1[k]被压入栈的时候,q1[i]已经被弹出栈.那么,q1[k]不会对q1[j]产生任何影响(这里可能有点乱,因为看起 来,当q1[j]<q1[k]的时候,是会有影响的,但实际上,这还需要另一个数r,满足j<k<r且 q1[r]<q1[j]<q1[k],也就是证明充分性的时候所说的情况…而事实上我们现在并不考虑这个r,所以说q1[k]对q1[j]没
有影响).
第二种情况下,我们可以发现这其实就是一个降序序列,所以所有数字都可以压入同一个栈.
这样,原命题的逆否命题得证,所以原命题得证.
此时,条件p为q1[i]和q1[j]不能压入同一个栈的充要条件也得证.
这样,我们对所有的数对(i,j)满足1<=i<j<=n,检查是否存在i<j<k满足p1[k]<p1[i]& lt;p1[j].如果存在,那么在点i和点j之间连一条无向边,表示p1[i]和p1[j]不能压入同一个栈.此时想到了什么?那就是二分图~
二分图的两部分看作两个栈,因为二分图的同一部分内不会出现任何连边,也就相当于不能压入同一个栈的所有结点都分到了两个栈中.
此时我们只考虑检查是否有解,所以只要o(n)检查出这个图是不是二分图,就可以得知是否有解.
然后处理最小字典序问题。实际上对二分图染色后对应的操作显然编号小的优先使用a操作,可以使得字典序尽量小。
这里要提二分图的一个性质:二分图中不同的连通分量染色是互不影响的。所以为了满足最小字典序的问题,可以选取编号最小的未染色结点染成1并对它所在连通分量染色。染完之后,模拟输出序列就可以了。
【代码】
#include<iostream>
#include<cstdlib>
#include<vector>
#include<stack>
using namespace std;
const int maxn = +; int S[maxn],C[maxn];
vector<int> G[maxn];
int n; void dfs(int u,int c){
C[u]=c;
for(int i=;i<G[u].size();i++){
int v=G[u][i];
if(C[v]==C[u]) {
cout<<; exit();
}
else if(!C[v]) dfs(v,-c);
}
}
int main() {
ios::sync_with_stdio(false); cin>>n;
for(int i=;i<=n;i++) cin>>S[i];
int _min[maxn]; _min[n+]=<<;
for(int i=n;i;i--) _min[i]=min(_min[i+],S[i]);
for(int i=;i<n;i++)
for(int j=i+;j<=n;j++) if(S[i]<S[j] && _min[j+]<S[i]) { //i<j<k && (S[k]<S[i]<S[j])
G[i].push_back(j); G[j].push_back(i);
} for(int i=;i<=n;i++) if(!C[i]) dfs(i,); int aim=;
stack<int> sta,stb;
for(int i=;i<=n;i++) {
if(C[i]==) {
sta.push(S[i]); cout<<"a ";
}
else {
stb.push(S[i]); cout<<"c ";
}
while(!sta.empty()&&sta.top()==aim || !stb.empty()&&stb.top()==aim) {
if(!sta.empty()&&sta.top()==aim){
sta.pop(); cout<<"b ";
}
else {
stb.pop(); cout<<"d ";
}
aim++;
}
}
return ;
}
NOIP2008 双栈队列的更多相关文章
- Luogu1155 NOIP2008 双栈排序 【二分图染色】【模拟】
Luogu1155 NOIP2008 双栈排序 题目描述 Tom最近在研究一个有趣的排序问题.如图所示,通过 2个栈 S1 和 S2 ,Tom希望借助以下 44 种操作实现将输入序列升序排序. 操作 ...
- C++实现一个简单的双栈队列
双栈队列的原理是用两个栈结构模拟一个队列, 一个栈A模拟队尾, 入队的元素全部压入此栈, 另一个栈B模拟队首, 出队时将栈A的元素弹入栈B, 将栈B的栈顶元素弹出 此结构类似汉诺塔, 非常经典, 这里 ...
- NOIP2008双栈排序[二分图染色|栈|DP]
题目描述 Tom最近在研究一个有趣的排序问题.如图所示,通过2个栈S1和S2,Tom希望借助以下4种操作实现将输入序列升序排序. 操作a 如果输入序列不为空,将第一个元素压入栈S1 操作b 如果栈S1 ...
- noip2008 双栈排序
题目描述 Description \(Tom\)最近在研究一个有趣的排序问题.如图所示,通过\(2\)个栈\(S_1\)和\(S_2\),\(Tom\)希望借助以下\(4\)种操作实现将输入序列升序排 ...
- Noip2008双栈排序
[问题描述] 用两个栈使一个1...n的排列变得有序.一共有四个操作: A.stack1.push() 读入一个放入栈一 B.stack1.pop() 弹出栈一放入输出序列 C.stack2.push ...
- NOIP2008双栈排序(贪心)
题目描述 Tom最近在研究一个有趣的排序问题.如图所示,通过2个栈S1和S2,Tom希望借助以下4种操作实现将输入序列升序排序. 操作a 如果输入序列不为空,将第一个元素压入栈S1 操作b 如果栈S1 ...
- [题解] [NOIP2008] 双栈排序——关系的冲突至图论解法
Problem 题目描述 Tom最近在研究一个有趣的排序问题.如图所示,通过2个栈S1和S2,Tom希望借助以下4种操作实现将输入序列升序排序. 操作a 如果输入序列不为空,将第一个元素压入栈S1 操 ...
- [NOIP2008]双栈排序 【二分图 + 模拟】
题目描述 Tom最近在研究一个有趣的排序问题.如图所示,通过2个栈S1和S2,Tom希望借助以下4种操作实现将输入序列升序排序. 操作a 如果输入序列不为空,将第一个元素压入栈S1 操作b 如果栈S1 ...
- [luogu1155 NOIP2008] 双栈排序 (二分图染色)
传送门 Description Input 第一行是一个整数 n . 第二行有 n 个用空格隔开的正整数,构成一个 1−n 的排列. Output 共一行,如果输入的排列不是"可双栈排序排列 ...
随机推荐
- 认识RGB和YUV
多年来,对于大部分人来说,对图形信号的认识不外有三种:射频信号,复合视频信号,S视频信号.射频信号是由复合视频信号调到高频上,普通电视机的天线输入信号用于射频信号,复合视频信号的输入出是用RGA端子. ...
- 【leetcode】Permutations II (middle)
Given a collection of numbers that might contain duplicates, return all possible unique permutations ...
- poj 1095 Trees Made to Order 卡特兰数
这题用到了卡特兰数,详情见:http://www.cnblogs.com/jackge/archive/2013/05/19/3086519.html 解体思路详见:http://blog.csdn. ...
- 移动wabAPP 开发 viewport 注意事项
我们在开发移动设备的网站时,最常见的的一个动作就是把下面这个东西复制到我们的head标签中: <meta name="viewport" content="widt ...
- SPRING IN ACTION 第4版笔记-第八章Advanced Spring MVC-006-Pizza例子的支付流程
一. 1. 2.payment-flow.xml <?xml version="1.0" encoding="UTF-8"?> <flow x ...
- POJ2965——The Pilots Brothers' refrigerator
The Pilots Brothers' refrigerator Description The game “The Pilots Brothers: following the stripy el ...
- NFS - Network File System网络文件系统
NFS(Network File System/网络文件系统): 设置Linux系统之间的文件共享(Linux与Windows中间文件共享采用SAMBA服务): NFS只是一种文件系统,本身没有传输功 ...
- 睡眠--TASK_INTERRUPTIBLE and TASK_UNINTERRUPTIBLE
http://i.cnblogs.com/EditPosts.aspx?opt=1 Two states are associated with sleeping, TASK_INTERRUPTI ...
- WinAPI——SetWindowsHookEx设置钩子说明
提示: 如果要设置系统级钩子, 钩子函数必须在 DLL 中. SetWindowsHookEx( idHook: Integer; {钩子类型} lpfn: TFNHookProc; {函数 ...
- 【HDOJ】4358 Boring counting
基本思路是将树形结构转线性结构,因为查询的是从任意结点到叶子结点的路径.从而将每个查询转换成区间,表示从该结点到叶子结点的路径.离线做,按照右边界升序排序.利用树状数组区间修改.树状数组表示有K个数据 ...