zoukankan      html  css  js  c++  java
  • 二分图匹配的判断+p1322+二分图最大匹配+tyvj1035

      二分图就是一个节点数>=2的图如果能被分成左右非空的两部分,且同一集合内部的边不相连,那么就是一个二分图.一张图是二分图,当且仅当图中不存在点数为奇数的环.因为链都好说,而环内相邻的点一定都不在同一个部分,但是点数为奇数时就会有点不能找到自己的位置...

      因为两个部分是等价的,对于一个图直接从一个节点出发dfs即可,复杂度n+m.

    using namespace std;
    bool dfs(int now){
        int tv=v[now];
        for(int j=head[now];j;j=e[j].next){
            if(v[e[j].y])
                if(v[e[j].y]+tv==3)
                    continue;
                else
                    return 0;
            else{    
                v[e[j].y]=3%tv;
                if(!dfs(e[j].y))return 0;
            }
            
        }
        return 1;
    }
    int main()
    {
        init();
        memset(v,0,sizeof(v));
        for(i=1;i<=n;i++)
        {
            if(!v[i])
            {    
                v[i]=1;
                if(!dfs(i))
                    break;
            }
        }
        if(i==n+1)
            cout<<"yes";
        else
            cout<<"no";
    }
    模板

      那么来看一个题吧:

           点我跳转

      并查集大概用的也是二分图的思想,考虑如何只用判断二分图的思想来做这道题.

      考虑到答案具有单调性,答案ans及更大的影响力都是可以达到的,更低的影响力则不能达到.那么可以开始二分答案了,其中的check()函数检查影响力最大为多少多少时能否构成二分图.具体实现只需要可以在那个dfs函数里向旁边dfs的时候加一句边长与当前边的比较即可.复杂度(m+n)log21e9.

    using namespace std;
    int i,xx,yy,vv;
    int n,m,tot,head[20010],v[20010];
    int l,r,mid;
    struct edge{
        int x,y,v;
        int next;
    }e[200010];
    void add(int x,int y,int v){
        tot++;
        e[tot].x=x;
        e[tot].y=y;
        e[tot].v=v;
        e[tot].next=head[x];
        head[x]=tot;
        return ;
    }
    bool dfs(int now,int vnow){
        int tv=v[now];
        for(int j=head[now];j;j=e[j].next){
            if(e[j].v<=vnow)continue;
            if(v[e[j].y])
                if(v[e[j].y]+tv==3)
                    continue;
                else
                    return 0;
            else{   
                v[e[j].y]=3-tv;
                if(!dfs(e[j].y,vnow))return 0;
            }
            
        }
        return 1;
    }
    bool check(int now){
        memset(v,0,sizeof(v));
        for(i=1;i<=n;i++){
            if(!v[i]){
                v[i]=1;
                if(!dfs(i,now))return 0;
            }
        }
        return 1;
    }
    int main(){
        n=read();m=read();
        for(i=1;i<=m;i++){
            xx=read();yy=read();vv=read();
            add(xx,yy,vv);
            add(yy,xx,vv);
            r=max(r,vv);
        }
        while(l+1!=r){
            mid=(l+r)>>1;
            if(check(mid)) r=mid;
            else           l=mid;
        }
        if(check(l))cout<<l;
        else        cout<<r;
        return 0;
    }
    View Code

      虽然看起来没有并查集优秀,但是总时间却比并查集算法快...

      

      "任意两条边都没有公共端点"的边的集合被称为图的一组匹配,在二分图中,包含边数最多的一组匹配被称为二分图的最大匹配.(算法竞赛进阶指南p394).

      由于性质:二分图的一组匹配是最大匹配当且仅当图中不存在增广路.这样就可以用匈牙利算法(增广路算法)求一个二分图的最大匹配了.复杂度(MN).

    bool dfs(int now){
        for(int j=head[now];j;j=e[j].next){
            if(!v[e[j].y]){
                v[e[j].y]=1;
                if(!match[e[j].y]||dfs(match[e[j].y])){
                    match[e[j].y]=now;
                    return 1;
                }
            }
        }
        return 0;
    }
    int main()
    {
        init();
        for(i=n;i<=n*n+n-1;i++){
            memset(v,0,sizeof(v));
            if(dfs(i))ans++;
        }
    }
    匈牙利算法

      看一道题:来自http://www.tyvj.cn/p/1035

      再来一个样例吧:

    4 6
    1 3
    1 4
    2 1
    2 3
    4 2
    4 4 

      再画一下图:

      青色是被禁止放置的点,红色是要放骨牌的地方.可以看出来答案是5.首先建图,然后跑一下增广路即可.匈牙利具体是怎么跑的呢?它每次从一个点进入dfs看相连的节点有没有还没有被匹配的点.如果有就可以利用它进行增广,并且返回1,或者去dfs相邻的点,如果它返回了1也可以用它进行增广.

    using namespace std;
    int i,f;
    int n,sum,ans,match[1111];//最大为n*n+n-1
    struct edge{
        int x,y;
        int next;
    }e[80010];//点数*方向数*双向n^2*4*2
    int tot,head[1111],v[1111],map[1111][1111];
    void add(int x,int y){
        tot++;
        e[tot].x=x;
        e[tot].y=y;
        e[tot].next=head[x];
        head[x]=tot;
        tot++;
        e[tot].x=y;
        e[tot].y=x;
        e[tot].next=head[y];
        head[y]=tot;
        return ;
    }
    bool dfs(int now){
        for(int j=head[now];j;j=e[j].next){
            if(!v[e[j].y]){
                v[e[j].y]=1;
                if(!match[e[j].y]||dfs(match[e[j].y])){
                    match[e[j].y]=now;
                    return 1;
                }
            }
        }
        return 0;
    }
    int main(){
        n=read();sum=read();
        for(;sum;sum--)
            map[read()][read()]=1;//如果你include<map>或者万能库,map就是关键字
        for(i=1;i<=n;i++){//建图
            for(f=1;f<=n;f++){
                if(map[i][f])continue;
                if(!map[i][f-1]&&f!=1)
                    add(i*n+f-1,i*n+f-2);
                if(!map[i][f+1]&&f!=n)
                    add(i*n+f-1,i*n+f);
                if(!map[i-1][f]&&i!=1)
                    add(i*n+f-1,i*n+f-1-n);
                if(!map[i+1][f]&&i!=n)
                    add(i*n+f-1,i*n+f-1+n);
            }
        }
        for(i=n;i<=n*n+n-1;i++){//跑匈牙利
            memset(v,0,sizeof(v));
            if(dfs(i))ans++;
        }
        cout<<ans/2;//ans是最大匹配中的点数,而每两个点能放一个骨牌
        return 0;
    }
    没有地方提交,只是过了样例而已
  • 相关阅读:
    linux命令行
    mybatis中#{}和${}的区别
    @InitBinder的作用
    mui 实用封装销毁页面
    【SQLite】简单的基本使用步骤
    常用的一些操作方法
    【HttpWeb】Post和GET请求基本封装
    【接口验证】特性验证参数
    小谈单例模式
    vs下开端口直接调试iis
  • 原文地址:https://www.cnblogs.com/qywyt/p/10223254.html
Copyright © 2011-2022 走看看