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

题目链接  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]存i之后最小的数,这样就可以把复杂度降到n的平方

之后就可以二分图染色了=-=,染色时要注意1-n不一定时联通的,所以要for一遍把所有都染掉

然后就是模拟了=-=字典序有点恶心,但只要时刻保证先出来在进去,先考虑s1,再考虑s2就可以了

代码:

#include<cstdio>
#include<cstdlib>
#include<stack>
#include<queue>
#include<cstring>
using namespace std;
const int N=1010;
int f[N],A[N];
struct node{
    int nu,ne;
}edge[N*N];
int tot,n;
int col[N],head[N],belong[N];
char ans[N*2];
bool vis[N];
void add(int a,int b)
{
    edge[++tot].nu=b;
    edge[tot].ne=head[a];
    head[a]=tot;
}
bool bfs(int st)
{
    queue<int>q;
    q.push(st);
    col[st]=1;
    vis[st]=true;
    while(!q.empty())
    {
        int a=q.front();
        q.pop();
        for(int i=head[a];i!=-1;i=edge[i].ne)
        {
            int k=edge[i].nu;
            if(col[k]==col[a])return false;
            if(vis[k]) continue;
            vis[k]=true;
            col[k]=col[a]==1?2:1;
            q.push(k);
        }
    }
    return true;
}
int main()
{
    memset(head,-1,sizeof(head));
    stack<int>s1,s2;
    int bj=1,cnt=0;
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&A[i]);
    memset(f,0x3f,sizeof(f));
    A[n+1]=1e7;
    for(int i=n;i>=1;i--)
        f[i]=min(f[i+1],A[i]);
    for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++)
            if(A[j]>A[i]&&A[i]>f[j+1])
                add(A[i],A[j]),add(A[j],A[i]);
    for(int i=1;i<=n;i++)
        if(!col[A[i]])
            if(!bfs(A[i]))
                return printf("0\n"),0;
    for(int i=1;i<=n;i++)
    {
        if(col[A[i]]==1)
            belong[A[i]]=1;
        if(col[A[i]]==2)
            belong[A[i]]=2;
    }
    for(int i=1;i<=n;i++)
    {
        int a=belong[A[i]];
        while(!s1.empty()&&s1.top()==bj)
        {
            bj++;
            s1.pop();
            ans[++cnt]=‘b‘;
        }
        while(!s2.empty()&&s2.top()==bj)
        {
            bj++;
            s2.pop();
            ans[++cnt]=‘d‘;
        }
        if(a==1)
        {
            s1.push(A[i]);
            ans[++cnt]=‘a‘;
        }
        if(a==2)
        {
            s2.push(A[i]);
            ans[++cnt]=‘c‘;
        }
    }
    while(!s1.empty()&&!s2.empty())
    {
        if(s1.top()<s2.top())
        {
            s1.pop();
            ans[++cnt]=‘b‘;
        }
        else
        {
            s2.pop();
            ans[++cnt]=‘d‘;
        }
    }
    while(!s1.empty())s1.pop(),ans[++cnt]=‘b‘;
    while(!s2.empty())s2.pop(),ans[++cnt]=‘d‘;
    for(int i=1;i<=cnt;i++)
        printf("%c ",ans[i]);
    return 0;
}

原文地址:https://www.cnblogs.com/greengenius/p/9780159.html

时间: 2024-11-06 09:40:10

双栈排序(二分图染色+模拟)的相关文章

NOIP2008 双栈排序 染色+模拟

挺不错的一道题,首先可以知道若存在形如 k<i<j 但 a[k]<a[i]<a[j]这样的,那么i,j一定不能(从始至终不能)进入同一个栈 例如 2 3 1,若2 3进入同一个栈,那么1再进栈然后马上出栈,这时候,2没有办法在3之前出来. 所以对于这样的i,j我们连一条边,然后dfs染色,若染色中发现相邻点颜色相同,则无解,否则我们按照1,2,1,2的顺序染色. 确定了每一个数属于哪个栈后,用2个stack模拟一下就好了. #include <iostream> #in

二分图 and code1170 双栈排序

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

双栈排序(二分图

# 题意通过两个栈,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>如果序

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

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),

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(

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)就是一个“可