二分图 and code1170 双栈排序

6.6二分图

二分图是这样一个图: 有两顶点集且图中每条边的的两个顶点分别位于两个顶点集中,每个顶点集中没有边直接相连接。

无向图G为二分图的充分必要条件是,G至少有两个顶点,且其所有回路的长度均为偶数。

判断二分图的常见方法是染色法: 开始对任意一未染色的顶点染色,之后判断其相邻的顶点中,若未染色则将其染上和相邻顶点不同的颜色, 若已经染色且颜色和相邻顶点的颜色相同则说明不是二分图,若颜色不同则继续判断,bfs和dfs都可以。

易知:任何无回路的的图均是二分图。

代码:

bool Color(int u)

{

for(int i=head[u]; ~i; i=edge[i].next)

{

int v = edge[i].to;

if(!col[v])

{

col[v] = !col[u];

if(!Color(v)) return false;

}

else if(col[v] == col[u])

return false;

}

return true;

}

Codevs1170 双栈排序NOIP2008

题目分析:

分析条件,我们把问题抽象为数学模型。设输入序列为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]. 证明略过,请参考sqybi.尝试后可以发现结论P是正确的.

把每个元素按照输入序列中的顺序编号,看作一个图中的每个顶点.这时,我们对所有的(i,j)满足i<j,判断是否满足结论P,即S[i],S[j]两个元素能否进入同一个栈.如果满足P,则在i,j之间连接一条边.

我们对图染色,由于只有两个栈,我们得到的图必须是二分图才能满足条件.由于要求字典序最小,即尽量要进入栈1,我们按编号递增的顺序从每个未染色的顶点开始染色,相邻的顶点染上不同的色,如果发生冲突,则是无解的.否则我们可以得到每个顶点颜色,即应该进入的栈.

接下来就是输出序列了,知道了每个元素的决策,直接模拟了.

在判断数对(i,j)是否满足P时,枚举检查是否存在k的时间复杂度是O(n),则总的时间复杂度是O(n^3),对于n=1000是太大了.这原因在于过多得枚举了k,我们可以用动态规划把枚举k变为O(1)的算法.

设F[i]为Min{S[i],S[i+1],S[i+2]..S[n-1],S[n]},状态转移方程为F[i]=Min{ S[i] , F[i+1] }.边界为F[N+1]=极大的值.

判断数对(i,j)是否满足P,只需判断(S[i]<S[j] 并且 F[j+1]<S[i])即可.时间复杂度为O(n^2).

多亏有题解,不然根本想不出来那个结论P啊...

模拟也是个问题:

知道了每个点的color(cc),入栈很容易。问题是什么时候出栈。

设a为当前需要出栈的元素,b为a+1

当s1的栈顶是a时,立刻出栈。因为出s1的优先级是2,只有入s1比它高,但那样就把应该出的元素压住了,显然不行。所以直接出就能保证字典序最小。

当s2的栈顶是a时,麻烦来了。因为出s2的优先级比入s1低,所以如果可能应当先入s1,把该出的元素放在s2不管它,直到迫不得已必须出的时候。

  s2出栈的情况:s2的栈顶是a时,cc[i+1]==2 || (cc[i+1]==1&&!s1.empty()&&s1.top()==b) || s1.empty()

注意模拟之前先cc[n+1]=2,保证入栈全部完成时s2不再受入栈限制(cc[i+1]==2)

详见代码:

#include<iostream>
#include<stack>
using namespace std;
const int Size=1005;

int n,data[Size];
int f[Size];
int cc[Size];
bool flag=true;
int a=1,b=2;
stack<int> s1,s2;
//edge
struct E{
    int to;
    E *next;
}*head[Size];
void add(int a,int b){
    E *p=new E;
    p->to=b; p->next=head[a];
    head[a]=p;
}

void color(int u){
    for(E* i=head[u]; i!=NULL; i=i->next){
        int v = i->to;
        if(!cc[v]){
            cc[v] = cc[u]==1?2:1;
            color(v);
            if(!flag)return;
        }
        else if(cc[v] == cc[u]){
            flag=false;
            return;
        }
    }
}

void out(int i){
    bool ok=true;
    while(ok){
        ok=false;
        if(!s1.empty()&&s1.top()==a){
            s1.pop(); cout<<"b "; a++;b++; ok=true;
        }
        if(!s2.empty()&&s2.top()==a){
            if(cc[i+1]==2||(cc[i+1]==1&&!s1.empty()&&s1.top()==b)||s1.empty()){
                s2.pop(); cout<<"d "; a++;b++; ok=true;
            }
        }
    }
}

int main(){
    cin>>n;
    for(int i=1;i<=n;i++)head[i]=NULL;
    for(int i=1;i<=n;i++){
        cin>>data[i];
    }
    f[n+1]=0x3f3f3f3f;
    for(int i=n;i>=1;i--){
        f[i]=min(data[i],f[i+1]);
    }

    for(int i=1;i<=n;i++){
        for(int j=i+1;j<=n;j++){
            if(data[i]<data[j]&&f[j+1]<data[i]){
                add(i,j); add(j,i);
            }
        }
    }

    for(int i=1;i<=n;i++){
        if(cc[i]==0){
            cc[i]=1;
            color(i);
            if(!flag)break;
        }
    }

    cc[n+1]=2;
    if(flag){
        for(int i=1;i<=n;i++){
            if(cc[i]==1)s1.push(data[i]),cout<<"a ";
            else s2.push(data[i]),cout<<"c ";
            out(i);
        }
    }
    else cout<<0;

    return 0;
} 
时间: 2024-10-18 17:36:50

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

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

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

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

双栈排序(二分图

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

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 双栈排序

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