双栈排序 二分图匹配

题目链接

https://www.acwing.com/problem/content/description/155/

题目

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)就是一个”可双栈排序序列”,而(2, 3, 4, 1)不是。

下图描述了一个将(1, 3, 2, 4)排序的操作序列:<a, c, c, b, a, d, d, b>

untitled.png

当然,这样的操作序列有可能有几个,对于上例(1, 3, 2, 4),<a, c, c, b, a, d, d, b>是另外一个可行的操作序列。

Tom希望知道其中字典序最小的操作序列是什么。

输入格式

输入的第一行是一个整数p,代表有p组测试数据。

每组测试数据的第一行有一个正整数n,第二行有n个用空格隔开的正整数,构成一个1~n的排列。

输出格式

输出共p行,如果输入的排列不是”可双栈排序排列”,输出数字0。

否则输出字典序最小的操作序列,每两个操作之间用空格隔开,行尾没有空格。

数据范围

1≤n≤1000

输入样例:

1
3
2 3 1

输出样例:

a c a b b d

题解

 二分图匹配问题。

考虑一下什么情况两个数不能放在同一个栈中:

如果i<j<k并且a[k]<a[i]<a[j], 那么a[i]和a[j]不能放在同一个栈中。因为a[i]>a[k] ,i<k.那么i必须等到a[k]出栈之后a[i]才能出栈,但是如果a[j]和a[i]在同一个栈中,因为j>i.那么a[j]在a[i]之前出栈,但a[j]>a[i],那么出栈后的元素就不是有序的了。

把不能放在同一个栈中的元素用边连起来,就变成一个二分图匹配问题,有边相连的两个点不能是同一种颜色。颜色分别为0和1,优先使用0染色,用0染色的表示a操作,用1染色的表示c操作。

代码

#include<algorithm>
#include <iostream>
#include<stack>
#include<cstring>
#include <cstdio>
using namespace std;
typedef long long ll;
const int maxn=1e3+5;
int g[maxn][maxn];
int a[maxn];
int minn[maxn];
int color[maxn];
int n;
bool dfs(int u,int x){
    color[u]=x;
    for(int i=1;i<=n;i++){
        if(g[u][i]){
            if(color[i]==x) return false;
            if(color[i]==-1&&!dfs(i,!x)) return false;
        }
    }
    return true;
}
int main(){
    int t;
    cin>>t;
    while(t--){
        cin>>n;
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        minn[n+1]=n+1;
        for(int i=n;i>=1;i--) minn[i]=min(minn[i+1],a[i]);
        memset(g,0,sizeof g);
        memset(color,-1,sizeof color);
        for(int i=1;i<=n;i++){
            for(int j=i+1;j<=n;j++){
                if(a[i]<a[j]&&minn[j+1]<a[i]){
                    g[i][j]=g[j][i]=1;
                }
            }
        }
        bool flag=true;
        for(int i=1;i<=n;i++){
            if(color[i]==-1){
                if(!dfs(i,0)){
                    flag=false;
                    break;
                }
            }
        }
        if(!flag){
            puts("0");
            continue;
        }
        stack<int> sta,sta2;
        int now=1;
        for(int i=1;i<=n;i++){
            if(color[i]==0){
                sta.push(a[i]);
                printf("a ");
            }else{
                sta2.push(a[i]);
                printf("c ");
            }
            while(true){
                if(sta.size()&&sta.top()==now){
                    sta.pop();
                    now++;
                    printf("b ");
                }else if(sta2.size()&&sta2.top()==now){
                    sta2.pop();
                    now++;
                    printf("d ");
                }else{
                    break;
                }
            }
        }
        printf("\n");
    }
    return 0;
}

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值