双栈排序(二分图

# 题意
通过两个栈,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>
如果序列可双栈排序,输出字典序最小的操作
否则输出数字0

# 题解

如果只有一个栈,则整个操作顺序是固定的:

从前往后遍历每个数,每次先将当前数压入栈中,如果后面的所有数均比栈顶元素大,则将栈顶弹出,否则栈顶不能被弹出。
因此,我们只需考虑将每个数分配给哪个栈即可。

这里有个很强的性质:

两个数 i , j ( i ≤ j ) 不能被放入同一个栈中,当且仅当存在 k , k > j 且 q [k] < q [i] < q [j]。

性质证明:
必要性:
如果有 i < j < k, 且 q[k] < q[i] < q[j],则因为 q[i] 和 q[j] 的后面均存在一个更小的q[k],因此q[i]和q[j]都不能从栈中被弹出,所以从栈底到栈顶的元素就不是单调的降序了,那么弹出时得到的序列就会出现逆序对。因此q[i]和q[j]不能被分到同一个栈中。
充分性:
如果q[i]和q[j]不满足上述条件,则我们在操作过程中一定不会出现矛盾。

在操作过程中,如果当前要入栈的数是q[j],那么此时:

所有大于q[j]的元素,都一定在栈中;
所有小于q[j]的元素,比如q[i] < q[j],则由于后面不存在q[k] < q[i],因此q[i]一定已经出栈;
所以此时将q[j]压入栈中后,从栈底到栈顶仍然可以保持降序,因此整个进栈、出栈过程是可以顺利进行的。

有了上述性质后,我们只需将所有满足条件的点分到两个栈中去即可。这一步可以转化成图论问题:

如果i, j满足条件,则在i和j之间连一条边。
然后判断是否是二分图即可。

答案要求字典序最小,因此从前往后染色时,需要优先将当前点分配到第一个栈中。

时间复杂度
建图时需要枚举所有点对,时间复杂度是O(n2)。
染色法判定二分图的时间复杂度是 O(n+m)。
最后模拟栈排序的过程需要 O(n) 的计算量。

因此总时间复杂度是 O(n2)。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int N=1010;
 4 int n;
 5 int a[N];
 6 int f[N];
 7 int color[N];
 8 bool g[N][N];
 9 stack<int>stk1,stk2;
10 bool dfs(int u,int c){
11    color[u]=c;
12    for(int i=1;i<=n;i++){
13       if(g[u][i]){
14          if(color[i] == c)
15             return 0;
16          if(color[i] == -1 && !dfs(i,!c)) return 0;
17       }
18    }
19    return 1;
20 }
21 int main(){
22    ios::sync_with_stdio(0);
23    cin.tie(0);
24    cout.tie(0);
25    cin>>n;
26    memset(g,0,sizeof g);
27    for(int i=1;i<=n;i++)
28       cin>>a[i];
29    f[n+1]=n+1;
30    for(int i=n;i>=1;i--)
31       f[i]=min(f[i+1],a[i]);
32
33    for(int i=1;i<=n;i++)
34       for(int j=i+1;j<=n;j++)
35          if(a[i] < a[j] && f[j+1] < a[i])
36             g[i][j]=g[j][i]=1;
37    memset(color,-1,sizeof color);
38    for(int i=1;i<=n;i++)
39       if(color[i]==-1 && !dfs(i,0)){
40          cout<<0<<endl;
41          return 0;
42       }
43    int now = 1;
44    for(int i = 1; i<=n;i++){
45       if(color[i]==0) {
46          stk1.push(a[i]);
47          cout<<‘a‘<<‘ ‘;
48       }
49       else {
50          stk2.push(a[i]);
51          cout<<‘c‘<<‘ ‘;
52       }
53
54       while(1){
55          if(stk1.size() && stk1.top()==now){
56             cout<<‘b‘<<‘ ‘;
57             stk1.pop();
58             now++;
59          }
60          else if(stk2.size() && stk2.top()==now){
61             cout<<‘d‘<<‘ ‘;
62             stk2.pop();
63             now++;
64          }
65          else break;
66       }
67    }
68    cout<<endl;
69 }

原文地址:https://www.cnblogs.com/hhyx/p/12495698.html

时间: 2024-10-02 23:48:35

双栈排序(二分图的相关文章

二分图 and code1170 双栈排序

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

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

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

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$连一条无向边. 显然必要条件时图不存在奇环,即能够二分染色. 再证明一下这是必要条件. 我们先构造

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

LG1155 双栈排序

题意 Tom最近在研究一个有趣的排序问题.如图所示,通过2个栈\(S_1\)和\(S_2\),Tom希望借助以下4种操作实现将输入序列升序排序. 操作a 如果输入序列不为空,将第一个元素压入栈\(S_1\) 操作b 如果栈\(S_1\)不为空,将\(S_1\)栈顶元素弹出至输出序列 操作c 如果输入序列不为空,将第一个元素压入栈\(S_2\) 操作d 如果栈\(S_2\)不为空,将\(S_2\)栈顶元素弹出至输出序列 如果一个1-n的排列P可以通过一系列操作使得输出序列为1,2,-,(n-1),

NOIP2008 双栈排序

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