[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_backusing 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_shadowfreopen ("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;}
转载于:https://www.cnblogs.com/zjp-shadow/p/9831491.html
[NOIp2008] 双栈排序 (二分图染色 + 贪心)相关推荐
- LOJ P1155 双栈排序 二分图染色 图论
https://www.luogu.org/problem/show?pid=P1155 题解: https://www.byvoid.com/zhs/blog/noip2008-twostack 开 ...
- NOIP2008 双栈排序
https://www.luogu.org/problem/show?pid=1155 题目描述 Tom最近在研究一个有趣的排序问题.如图所示,通过2个栈S1和S2,Tom希望借助以下4种操作实现将输 ...
- P1155 双栈排序(二分图染色)
P1155 双栈排序(二分图染色) 题目描述 Tom最近在研究一个有趣的排序问题.如图所示,通过2个栈S1和S2,Tom希望借助以下4种操作实现将输入序列升序排序. 操作a 如果输入序列不为空,将第一 ...
- P1155 双栈排序(二分图的染色判断+链式前向星)
P1155 双栈排序 让字典序最小,当然尽量进S1 那什么时候必须进S2呢? a[i]和a[j] 不能压入同一个栈⇔存在一个k,使得i<j<k且a[k]<a[i]<a[j] 因 ...
- 虚拟化构建二分图(BZOJ2080 题解+浅谈几道双栈排序思想的题)
虚拟化构建二分图 ------BZOJ2080 题解+浅谈几道双栈排序思想的题 本题的题解在最下面↓↓↓ 不得不说,第一次接触类似于双栈排序的这种题,是在BZOJ的五月月赛上. [BZOJ4881][ ...
- #include NOIP2008 Junior 双栈排序 ——using namespace wxl;
题目描述 Tom最近在研究一个有趣的排序问题.如图所示,通过2个栈S1和S2,Tom希望借助以下4种操作实现将输入序列升序排序. 操作a 如果输入序列不为空,将第一个元素压入栈S1 操作b 如果栈S1 ...
- #include lt;NOIP2008 Juniorgt; 双栈排序 ——using namespace wxl;
题目描述 Tom最近在研究一个有趣的排序问题.如图所示,通过2个栈S1和S2,Tom希望借助以下4种操作实现将输入序列升序排序. 操作a 如果输入序列不为空,将第一个元素压入栈S1 操作b 如果栈S1 ...
- 二分图——洛谷P1155 双栈排序
https://daniu.luogu.org/problem/show?pid=1155 二分图染色+模拟 1.首先考虑一个简单情况--单栈排序,显然有这样的一个事实: a[i]和a[j] 不能压入 ...
- 【每日一题】8月7日题目精讲—双栈排序
来源:牛客网 文章目录 题目描述 题意: 题解: 代码: 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 131072K,其他语言262144K 64bit IO Format: %l ...
最新文章
- Calendar类点点滴滴积累
- 三维感知,这些干货足够了!(自动驾驶/三维重建/SLAM/点云/标定/深度估计/3D检测)...
- 【原创】new和delete
- DotNET企业架构应用实践-企业管理软件架构的历史与发展(中)- 分布式系统
- 《SAP CRM管理与实施指南》一一2.2 SAP CRM基础功能
- DIB位图(Bitmap)的读取和保存
- AV Linux 2016系统今年发布:影音制作专用
- 一文搞定 Spring Data Redis 详解及实战
- truncate table语句和delete table语句的区别
- 小熊的人生回忆(二)
- 电子相册系统(六)相片列表显示
- 用 grldr 引导WinXP/Ubuntu双系统
- eclipse报错 : One or more constraints have not been satisfied.
- 注册cad中文件未找到html,acad(2014CAD出现未找到acad文件 求解)
- HNOI2015 亚瑟王
- 社交媒体中有哪些有趣的数据?能挖掘出哪些价值?
- 第九章 思科IOS与华为VRP系统及命令行配置
- cent os 安装
- appcan.frame.open打开的浮空窗口页面不开启弹动的情况下,也会有微微的滑动bug...
- 微型计算机48MHz辐射超,造成EMC辐射超标原因有哪些(精彩案例分析)
热门文章
- 服务器计时器、Windows 计时器和线程计时器
- 【Vue.js 知识量化】基础语法
- 【Spring MVC】学习笔记汇总
- RedHat停止维护CentOS!CentOS 创建者发起新项目,刚上线空白项目Star数已破两千
- arcgis 4.x graphicslayer点击事件_ArcGis中X、Y值的巧用方法小记
- 最近给公司撸了一个可视化大屏
- 数据中台不是企业的万能妙药
- 财务报表开发实例分析:几个通用维度介绍与关键点
- 数据/方法论固然重要,但人为分析更有价值!
- Nodejs版本的企业微信中接收消息与腾讯对接之验证URL 代码已经上传,可以去下载