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 }
posted @ 2018-10-12 22:40 阿波罗2003 阅读(...) 评论(...) 编辑 收藏