[NOIp2008] 双栈排序 (二分图染色 + 贪心)
题意
给你一个长为 \(n\) 的序列 \(p\) ,问是否能够通过对于两个栈进行 push, pop(print)
操作使得最后输出序列单调递增(即为 \(1 \cdots n\) ),如果无解输出 \(0\) 。
每个操作有个优先级,push(1) > pop(1) > push(2) > pop(2)
,输出优先级最大的一组解。
\(n \le 1000\)
题解
有兴趣可以来逛逛 我的博客。
洛谷前面大部分题解,对于后面直接模拟的思路肯定是错的,本文介绍一个基于贪心的算法(不知道对不对,因为没有强数据验证)。
首先考虑只有一个栈的时候如何解决这个问题。
就是对于一对位置 \((i, j)\) 是否能共存三个位置 \(i < j < k\) 存在 \(p_k < p_i < p_j\) 是不可行的,因为 \(p_k\) 需要在 \(p_i\) 与 \(p_j\) 之前出栈,但 \(p_i\) 又需要在 \(p_j\) 之前出栈,那么这就会产生矛盾。
我们预处理 \(\displaystyle f_i = \min_{j = i}^{n} p_j\) ,就可以在 \(O(n ^ 2)\) 的时间内判断一对 \(i, j\) 是否可以共存了(也就是 \(f_{j + 1} < p_i < p_j\) )
然后对于存在两个栈的情况,我们就需要把 \(p\) 划分成两个序列,使得这两个序列之中的数都互不冲突。
这样的话,我们对于一对不能共存的 \(i, j\) 连边,然后进行二分图染色。如果不可染,那么就是不存在一组合法解。
之后我们只需要解决使得最后解字典序最小的限制。
我们染色的时候 BFS
染色,尽量把在前面的放入第一个栈。
然后后面得到操作序列直接模拟肯定是个错的。
举个样例:
5
2 4 1 3 5
标准输出:
a c a b b a b a d b
前面大部分错误的输出:
a c a b b a b d a b
为什么呢,因为你向第二个栈 push
后,不一定现在拿出来 pop
,第一个栈中能继续 push
。
那么我们就贪心一下,我们在 push
之后不马上 pop
,等到需要 pop
的时候再 pop
。
哪些时候需要 pop
呢,就是这个栈不合法的时候需要 pop
(也就是这个栈 栈顶到栈底 不单调递增的时候,不满足单调栈性质)
但是注意向第二个栈中 push
之前,因为第一个栈的 pop
优先级更高,我们看能不能先 pop
第一个栈。
这样就应该是最优的了,注意最后要把两个栈按顺序清空。
代码
#include <bits/stdc++.h>
#define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Set(a, v) memset(a, v, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define debug(x) cout << #x << ": " << (x) << endl
#define DEBUG(...) fprintf(stderr, __VA_ARGS__)
#define pb push_back
using namespace std;
template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; }
template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; }
inline int read() {
int x(0), sgn(1); char ch(getchar());
for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
return x * sgn;
}
void File() {
#ifdef zjp_shadow
freopen ("P1155.in", "r", stdin);
freopen ("P1155.out", "w", stdout);
#endif
}
const int N = 1010, inf = 0x7f7f7f7f;
int n, P[N], minv[N], col[N];
int pos = 1;
stack<int> S[2];
inline void out(char ch) {
putchar (ch); putchar (' ');
}
inline bool Pop(int id) {
if (!S[id].empty() && S[id].top() == pos) {
out(id ? 'd' : 'b'), S[id].pop(), ++ pos;
return true;
}
return false;
}
inline void Push(int cur, int id) {
if (id == 1) { while(Pop(0)); }
while (!S[id].empty() && S[id].top() < cur)
if (!Pop(id)) Pop(id ^ 1);
if (id == 1) { while(Pop(0)); }
S[id].push(cur); out(id ? 'c' : 'a');
}
vector<int> G[N];
int main () {
File();
n = read();
For (i, 1, n)
P[i] = read();
minv[n + 1] = n + 1;
Fordown (i, n, 1)
minv[i] = min(minv[i + 1], P[i]);
For (i, 1, n) For (j, i + 1, n)
if (minv[j + 1] < P[i] && P[i] < P[j])
G[i].pb(j), G[j].pb(i), col[i] = col[j] = -1;
For (i, 1, n) if (!~col[i]) {
queue<int> Q; Q.push(i); col[i] = 0;
while (!Q.empty()) {
int u = Q.front(); Q.pop();
for (int v : G[u]) {
if (~col[v] && col[v] != (col[u] ^ 1)) return puts("0"), 0;
if (!~col[v]) Q.push(v);
col[v] = col[u] ^ 1;
}
}
}
For (i, 1, n)
Push(P[i], col[i]);
bool flag = true;
while (flag) {
flag = false;
while(Pop(0)) flag = true;
while(Pop(1)) flag = true;
}
return 0;
}
[NOIp2008] 双栈排序 (二分图染色 + 贪心)的更多相关文章
- NOIP2008双栈排序[二分图染色|栈|DP]
题目描述 Tom最近在研究一个有趣的排序问题.如图所示,通过2个栈S1和S2,Tom希望借助以下4种操作实现将输入序列升序排序. 操作a 如果输入序列不为空,将第一个元素压入栈S1 操作b 如果栈S1 ...
- [luogu1155 NOIP2008] 双栈排序 (二分图染色)
传送门 Description Input 第一行是一个整数 n . 第二行有 n 个用空格隔开的正整数,构成一个 1−n 的排列. Output 共一行,如果输入的排列不是"可双栈排序排列 ...
- LOJ P1155 双栈排序 二分图染色 图论
https://www.luogu.org/problem/show?pid=P1155 题解: https://www.byvoid.com/zhs/blog/noip2008-twostack 开 ...
- Luogu1155 NOIP2008 双栈排序 【二分图染色】【模拟】
Luogu1155 NOIP2008 双栈排序 题目描述 Tom最近在研究一个有趣的排序问题.如图所示,通过 2个栈 S1 和 S2 ,Tom希望借助以下 44 种操作实现将输入序列升序排序. 操作 ...
- $[NOIp2008]$双栈排序 栈/二分图/贪心
\(Sol\) 先考虑单栈排序,怎么样的序列可以单栈排序呢?设\(a_i\)表示位置\(i\)是哪个数.\(\exist i<j<k\),都没有\(a_k<a_i<a_j\), ...
- [NOIP2008]双栈排序 【二分图 + 模拟】
题目描述 Tom最近在研究一个有趣的排序问题.如图所示,通过2个栈S1和S2,Tom希望借助以下4种操作实现将输入序列升序排序. 操作a 如果输入序列不为空,将第一个元素压入栈S1 操作b 如果栈S1 ...
- NOIP2008双栈排序(贪心)
题目描述 Tom最近在研究一个有趣的排序问题.如图所示,通过2个栈S1和S2,Tom希望借助以下4种操作实现将输入序列升序排序. 操作a 如果输入序列不为空,将第一个元素压入栈S1 操作b 如果栈S1 ...
- [题解] [NOIP2008] 双栈排序——关系的冲突至图论解法
Problem 题目描述 Tom最近在研究一个有趣的排序问题.如图所示,通过2个栈S1和S2,Tom希望借助以下4种操作实现将输入序列升序排序. 操作a 如果输入序列不为空,将第一个元素压入栈S1 操 ...
- noip2008 双栈排序
题目描述 Description \(Tom\)最近在研究一个有趣的排序问题.如图所示,通过\(2\)个栈\(S_1\)和\(S_2\),\(Tom\)希望借助以下\(4\)种操作实现将输入序列升序排 ...
随机推荐
- R语言绘制直方图,
直方图: 核密度函数: 练习题目1: 绘制出15位同学体重的直方图和核密度估计图,并与正态分布的概率密度函数作对比 代码如下: > w <- c(75.0, 64.0, 47.4, 66. ...
- fileInput插件上传文件
一.ftl <form action="" method="post" name="form" id="form" ...
- 【问题解决方案】之 hadoop 用jps命令后缺少namenode的问题
用Xshell连接腾讯cloud里的虚拟机后,jps命令查无namenode导致过滤排序程序跑不起来,如图: 解决方案: Google之,说需要重启,格式化后再启动Hadoop.但鉴于本人不知道实现的 ...
- I/O中断处理详细过程
1.CPU发送启动I/O设备的命令,将I/O接口中的B触发器置1,D触发器置O. 2.设备开始工作,需要向CPU传送数据时,将数据送入数据缓冲器中. 3.输入设备向I/O接口发出“设备工作结束”的信号 ...
- #Leetcode# 985. Sum of Even Numbers After Queries
https://leetcode.com/problems/sum-of-even-numbers-after-queries/ We have an array A of integers, and ...
- 常见的 CSRF、XSS、sql注入、DDOS流量攻击
CSRF攻击 :跨站请求伪造攻击 ,CSRF全名是Cross-site request forgery,是一种对网站的恶意利用,CSRF比XSS更具危险性 攻击者一般会使用吸引人的图片去引导用户点击进 ...
- MySQL数据库性能优化思路与解决方法(一转)
1.选取最适用的字段属性 MySQL可以很好的支持大数据量的存取,但是一般说来,数据库中的表越 小,在它上面执行的查询也就会越快.因此,在创建表的时候,为了获得更好的性能,我们可以将表中字段的宽度设 ...
- Bootstrap知识记录:表单和图片
一.表单Bootstrap 提供了一些丰富的表单样式供开发者使用.1.基本格式//实现基本的表单样式<form><div class="form-group"&g ...
- 五句话搞定JavaScript作用域(ES5)
JavaScript的作用域一直以来是前端开发中比较难以理解的知识点,对于JavaScript的作用域主要记住几句话,走遍天下都不怕... 一.“JavaScript中无块级作用域” 在Java或C# ...
- python爬虫之爬虫性能篇
一.首先想到的是for循环,单线程爬取每个url,但是如果有url出现了问题,后面的url就得等,性能低. 二.我们考虑线程池的问题,下面我们定义了线程池里面最多10个任务,也就是说最多同一时间只能有 ...