【图-二分图染色】NOIP提高组2008双栈排序

【问题描述】

Tom最近在研究一个有趣的排序问题。如图所示,通过2个栈S1和S2,Tom希望借助以下4种操作实现将输入序列升序排序。

操作a
如果输入序列不为空,将第一个元素压入栈S1
操作b
如果栈S1不为空,将S1栈顶元素弹出至输出序列
操作c
如果输入序列不为空,将第一个元素压入栈S2
操作d
如果栈S2不为空,将S2栈顶元素弹出至输出序列

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

【题目分析】
1.暴力解法
维护俩个单调的栈,对于每一个元素,显然可以进行如下操作顺序
优先判断S1里面第一个元素能否能出栈,若能出栈,则当前最优解一定是b操作
然后判断当前元素x是否能进入S1栈,若能则试验a操作(未必最优)
再判断S2里面第一个元素能否能出栈,若能出栈,则当前最优解一定是d操作
最后试验将x放入S2

2.优化:自己手动数据四五次后找到了规律:设输入序列为S,考虑S[i],S[j]两个元素不能进入同一个栈的条件.注意,这里所说的”S[i],S[j]两个元素不能进入同一个栈”,不是说仅仅不能同时在一个栈中,而是自始至终不能进入一个栈,即如果有解,那么S[i],S[j]一定进入过的栈不同.

结论P: S[i],S[j]两个元素不能进入同一个栈 <=> 存在k,满足i < j < k,使得S[k] < S[i] < S[j].
这里注意要对于k的枚举进行预处理

#include <iostream>
#include <cstdio>
using namespace std;
int n,now=1,a[1001],least[1002],color[1001],map[1001][1001];  bool used[1001];  char caozuo[2010];  int q1[1001],q2[1001],top1,top2; bool shutcolor,shut;
void read()
{
    int i;
    cin>>n;
    for (i=1;i<=n;i++)
        cin>>a[i];
    least[n]=a[n];
    for (i=n-1;i>=1;i--)
        least[i]=min(least[i+1],a[i]);
    q1[0]=1001;   q2[0]=1002;  now=1;
    top1=1;    top2=1;
    return;
}
void getcolor(int k,int c)
{
    if (shutcolor)
        return;
    if (used[k])
    {
        if (c==color[k])
            return;
        else
        {
            shutcolor=1;
            return;
        }
    }
    used[k]=1;  color[k]=c;
    int i,need;
    if (c==1)  need=2;
    else need=1;
    for (i=1;i<=map[k][0];i++)
        getcolor(map[k][i],need);
    return;
}
void getmap()
{
    int i,j;
    for (i=1;i<=n;i++)
        for (j=i+1;j<n;j++)
        if (a[j]>a[i])
        if (least[j+1]<a[i])
        {
            map[i][0]++;
            map[i][map[i][0]]=j;
            map[j][0]++;
            map[j][map[j][0]]=i;
        }
    for (i=1;i<=n;i++)
    if (used[i]==0)
    getcolor(i,1);
    return;
}
void print()
{
    int i;
    for (i=1;i<2*n;i++)
        cout<<caozuo[i]<<" ";
    cout<<caozuo[2*n];
    return;
}
void work(int k,int u)
{
    if (shut)
        return;
    if (u==2*n+1)
    {
        shut=1;
        print();
        return;
    }
    if (now==q1[top1])
    {
        caozuo[u]='b';
        top1--; now++;
        work(k,u+1);
        top1++; now--;
    }
    else if (color[k]==1)
    {
        caozuo[u]='a';
        top1++;
        q1[top1]=a[k];
        work(k+1,u+1);
        top1--;
    }
    if (now==q2[top2])
    {
        caozuo[u]='d';
        top2--;  now++;
        work(k,u+1);
        top2++;  now--;
    }
    else if (color[k]==2)
    {
        caozuo[u]='c';
        top2++;
        q2[top2]=a[k];
        work(k+1,u+1);
        top2--;
    }
    return;
}
void judge()
{
    int i;
    for (i=1;i<=n;i++)
        if (color[i]==0)
        color[i]=1;
    if (shutcolor==0)
        work(1,1);
    else
        cout<<0;
    return;
}
int main()
{
    freopen("twostack.in","r",stdin);
    freopen("twostack.out","w",stdout);
    read();
    getmap();
    judge();
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值