vijos 1605 双栈排序 - 贪心 - 二分图

题目传送门

  传送门I

  传送门II

题目大意

  双栈排序,问最小字典序操作序列。

  不能发现两个数$a_{j}, a_{k}\ \ (j < k)$不能放在同一个栈的充分必要条件时存在一个$i$使得$j < k < i$且$a_{i} < a_{j} < a_{k}$。

  证明?写个dfs就证完了(就是考虑任意一个三元组)。

  然后可建图,对于满足上面条件的$(j, k)$,$j$和$k$连一条无向边。

  显然必要条件时图不存在奇环,即能够二分染色。

  再证明一下这是必要条件。

  我们先构造一个做法:硬点二分染色完白点扔第一个栈里,黑点扔第二个栈里,当当前需要输出的数能够输出的时候就输出,否则继续加数。

  我们只需要证明每个数都能被正确的输出就行了。

  显然第1个数一定能被正确地输出。

  假设前$k - 1\ \ \ (k > 1)$个数已经被正确地输出了,考虑第$k$个数。

  • 第$k$个数不在栈中,那么还剩在序列中,我们把之间的数加入栈中,再把它加入弹出,它就正确地输出了。
  • 第$k$个数在栈中
    • 某个栈的栈顶,直接弹出,正确地输出了。
    • 否则它上面一定存在一个数$x > k$使得它无法弹出。
      • 如果$1, 2, \dots, k - 1$中都在$k$之前,那么输出完前$k - 1$个数后,$k$入栈后就可以弹出并输出了,与做法矛盾。
      • 否则存在一个$y < x$在$k$之后,由建图方法和染色可知$k, x$不能在同一个栈中,矛盾。

  因此图能够二分染色是存在解的充分必要条件。

  然后拿个栈直接贪心就好了。每次选择能够进行的字典序最小的操作。

Code

 1 /**
 2  * Vijos
 3  * Problem#1605
 4  * Accepted
 5  * Time: 42ms
 6  * Memory: 1.363m
 7  */
 8 #include <iostream>
 9 #include <cstdlib>
10 #include <cstring>
11 #include <cstdio>
12 using namespace std;
13 typedef bool boolean;
14
15 const int N = 1e3 + 5;
16
17 int n;
18 int ar[N];
19 int mns[N];
20 boolean g[N][N];
21
22 inline void init() {
23     scanf("%d", &n);
24     for (int i = 1; i <= n; i++)
25         scanf("%d", ar + i);
26 }
27
28 int col[N];
29 boolean color(int p, int col) {
30     if (::col[p] != -1)
31         return ::col[p] == col;
32     ::col[p] = col;
33     for (int i = 1; i <= n; i++)
34         if (g[p][i] && !color(i, col ^ 1))
35             return false;
36     return true;
37 }
38
39 int s1[N], s2[N];
40 int tp1, tp2;
41
42 inline void solve() {
43     mns[n] = ar[n];
44     for (int i = n - 1; i; i--)
45         mns[i] = min(mns[i + 1], ar[i]);
46     for (int i = 1; i < n; i++)
47         for (int j = i + 1; j < n; j++)
48             if (ar[i] < ar[j] && mns[j] < ar[i])
49                 g[i][j] = g[j][i] = true;
50     memset(col, -1, sizeof(col));
51     for (int i = 1; i <= n; i++)
52         if (col[i] == -1 && !color(i, 0)) {
53             puts("0");
54             return;
55         }
56
57     int cur = 1, p = 1;
58     while (cur <= n) {
59         if (p <= n && !col[p] && s1[tp1] != cur)
60             putchar(‘a‘), s1[++tp1] = ar[p++];
61         else if (s1[tp1] == cur)
62             putchar(‘b‘), tp1--, cur++;
63         else if (p <= n && col[p] && s2[tp2] != cur)
64             putchar(‘c‘), s2[++tp2] = ar[p++];
65         else
66             putchar(‘d‘), tp2--, cur++;
67         putchar(‘ ‘);
68     }
69 }
70
71 int main() {
72     init();
73     solve();
74     return 0;
75 }

原文地址:https://www.cnblogs.com/yyf0309/p/9781144.html

时间: 2024-10-09 21:18:51

vijos 1605 双栈排序 - 贪心 - 二分图的相关文章

[LuoguP1155]双栈排序_二分图_bfs

双栈排序 题目链接:https://www.luogu.org/problem/P1155 数据范围:略. 题解: 神仙题. 就第一步就够劝退了. 这个二分图非常不容易,首先只有两个栈,不是属于一个就是属于另一个,我们用二分图判断冲突. 然后不能模拟,我们就贪心的bfs就行了,这一步很鬼畜啊.... 原文地址:https://www.cnblogs.com/ShuraK/p/11762283.html

双栈排序(二分图

# 题意通过两个栈,4中操作,实现输入序列升序排序操作a:如果输入序列不为空,将第一个元素压入栈S1操作b:如果栈S1不为空,将S1栈顶元素弹出至输出序列操作c:如果输入序列不为空,将第一个元素压入栈S2操作d:如果栈S2不为空,将S2栈顶元素弹出至输出序列如果一个1~n的排列P可以通过一系列操作使得输出序列为1, 2,…,(n-1), n,Tom就称P是一个”可双栈排序排列”. 操作序列为<a,c,c,b,a,d,d,b>另一个可行的序列为<a,c,c,b,a,d,d,b>如果序

双栈排序(二分图染色+模拟)

题目链接  https://www.luogu.org/problemnew/show/P1155 一开始我还以为能直接模拟=-=,太天真了...写了好久对了3个点...看了题解之后恍然大悟,先二分图匹配一下确定每个点在哪个栈里头再模拟会轻松很多.可怎么建边呢? 我们先从单栈排序开始: 可以的出的是,对于i,j,若存在k使得i<j<k,且A[k]<A[i]<A[j]的时候,是一定不能单栈排序的 就像3 4 1这样的数,因为要保证小的先出来, 我们可以在这样的i,j间建边,用f[i]

【Luogu】P1155双栈排序(二分图)

题目链接在此 此题一开始写了个深搜,过了30%的数据,也就是n<=10的那一段.... 然后看了题解发现这是个二分图的判断. 我们先举例子找到不能放进一个栈里的规律.设有数列[2,3,1,4] 容易模拟得到这个数列单栈是搞不出来的.为什么搞不出来呢? 如果输入的序列是单调递减的,自然一个栈可以搞定.直接全部压栈再全部弹栈就可以了. 如果输入的序列不是单调递减,一个栈也有可能搞定.但是像我们举的这个例子,你压3的时候必须把2压在底下,那怎么先出2后出3呢? 继续模拟能够发现规律:若i.j不能单栈,

二分图 and code1170 双栈排序

6.6二分图 二分图是这样一个图: 有两顶点集且图中每条边的的两个顶点分别位于两个顶点集中,每个顶点集中没有边直接相连接. 无向图G为二分图的充分必要条件是,G至少有两个顶点,且其所有回路的长度均为偶数. 判断二分图的常见方法是染色法: 开始对任意一未染色的顶点染色,之后判断其相邻的顶点中,若未染色则将其染上和相邻顶点不同的颜色, 若已经染色且颜色和相邻顶点的颜色相同则说明不是二分图,若颜色不同则继续判断,bfs和dfs都可以. 易知:任何无回路的的图均是二分图. 代码: bool Color(

AC日记——双栈排序 洛谷 P1155

双栈排序 思路: 二分图染+模拟: 代码: #include <bits/stdc++.h> using namespace std; #define maxn 1005 #define maxm 2000005 int n,head[maxn],E[maxm],V[maxm],cnt,col[maxn]; int minn[maxn],ai[maxn],sta1[maxn],sta2[maxn],top1,top2; bool if_[maxn][maxn]; inline void in(

洛谷——P1155 双栈排序

题目描述 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)就是一个“可

P1155 双栈排序

P1155 双栈排序 题目描述 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是一个"可双栈排序排列".例

Noip2008双栈排序

[问题描述] 用两个栈使一个1...n的排列变得有序.一共有四个操作: A.stack1.push() 读入一个放入栈一 B.stack1.pop() 弹出栈一放入输出序列 C.stack2.push() 读入一个放入栈二 D.stack2.pop() 弹出栈二放入输出序列 给你一个初始的排列,求一个字典序最小的操作序列使得变得有序,若没有满足条件的操作序列,输出'0'. Sample.in                                Sample.out 4     1 3