zoukankan      html  css  js  c++  java
  • 二分图相关

    概念

    对于一个图(G=(V,E)),若能将其点集分为两个互不相交的两个子集(X、Y)
    使得(X∩Y=phi),且对于(G)的边集(V),若其所有边的顶点全部一侧属于(X)
    一侧属于(Y),则称图(G)为一个二分图。

    定理

    当且仅当无向图(G)的回路个数为偶数时,图(G)为一个二分图。
    无回路的图也是二分图。

    判定

    在二分图(G)中,任选一个点(V)
    使用(BFS)预处理出其他点相对于(V)的距离(边权为(1)
    对于每一条边(E),枚举它的两个端点,若其两个端点的值,
    一个为奇数,一个为偶数,则图(G)为一个二分图。

    匹配

    对于一个二分图(G)的子图(M)(前提是在二分图中),若(M)的边集(E)的的任意两条边都不连接同一个顶点,
    则称(M)(G)的一个匹配。特别的,其中边数最多的(M)称为二分图的最大匹配。

    匈牙利算法

    一、算法描述

    建立有向图(G),分为二分图的左侧和右侧。
    优先选择左侧序号更小的连接可能的边。
    对于两个点的目标点“冲突”的时候,采取“协商”的办法。
    即序号小的连接可能连接的另一条边。
    若“协商”失败,则放弃序号较大的点的边。

    二、步骤

    推荐看这篇大佬的博客千古神犇zay 以上内容基本来自这里。

    三、总结

    (1))如果后来的和以前的发生矛盾,则以前的优先退让。
    (2))如果以前的退让之后自己无法匹配,则以前的拒绝退让,新来的去寻找下一个匹配。
    (3))如果新来的谁也匹配不上了,那就这么单着吧。
    所以说做法已经很清晰了,直接上代码吧。

    (Code)

    //二分图匹配-匈牙利算法  1000ms
    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #define maxn 2005
    #define re register
    using namespace std;
    int n,m,es,x,y,cnt,head[maxn];
    inline int read()
    {
    	int x=0,f=1; char ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    	return x*f;
    	
    }
    int ans,used[maxn];
    bool vis[maxn];
    struct Edge{
    	int v,nxt;
    }e[maxn*maxn];
    inline void add(int u,int v)
    {
    	e[++cnt].v=v;
    	e[cnt].nxt=head[u];
    	head[u]=cnt;
    } 
    bool dfs(int x)
    {
    	for(int i=head[x];i;i=e[i].nxt)
    	{
    		int ev=e[i].v;
    		if(!vis[ev])
    		{
    			vis[ev]=true;
    			if(used[ev]==0||dfs(used[ev]))//要注意这里dfs的是used[ev]而不是ev
    			{
    				used[ev]=x;
    				return true;
    			}
    		}
    	}
    	return false;//注意这里返回false,否则会TLE
    }
    void Maxmatch()
    {
    	for(re int i=1;i<=n;++i)
    	{
    		memset(vis,false,sizeof(vis));
    		ans+=dfs(i);
    	}
    }
    int main()
    {
    	n=read(),m=read(),es=read();
    	for(re int i=1;i<=es;++i)
    	{
    		x=read(),y=read();
    		if(x>n||y>m) continue;//这是题目的锅
    		add(x,y); 
    	}
    	Maxmatch();
    	printf("%d
    ",ans);
    	return 0; 
    }
    

    最大流(Dinic)做法

    //网络最大流Dinic做法 
    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<queue>
    #include<cstring>
    #define maxn 20005
    #define maxm 2000010
    using namespace std;
    const int INF =999999999;
    queue<int> q;
    int kx[maxm],ky[maxm],cnt1;
    int n,m,x,y,z,es,maxflow,deep[maxn];
    int s,t;
    struct Edge{
        int v,nxt=-1,w;
    }e[maxm<<2];
    int cnt=-1,head[maxn];
    void add(int x,int y,int z)
    {
        e[++cnt].v=y;
        e[cnt].w=z;
        e[cnt].nxt=head[x];
        head[x]=cnt;
    }
    
    int BFS()
    {
        memset(deep,0,sizeof(deep));//deth记录深度 
        while(!q.empty())
            q.pop();
        deep[s]=1;
        q.push(s);
         while(!q.empty())
        {
            int u=q.front();
            q.pop();
            for(int i=head[u];i!=-1;i=e[i].nxt)
            {
                if(e[i].w>0&&deep[e[i].v]==0)//分层 
                {
                    deep[e[i].v]=deep[u]+1;
                    q.push(e[i].v);
                }
            }
        }
       
        if(deep[t]!=0)//当汇点的深度不存在时,说明不存在分层图,同时也说明不存在增广路
            return 1;
        else
            return 0;
    }
    int dfs(int now,int limit)
    {
        if(now==t) return limit;
        for(int i=head[now];i!=-1;i=e[i].nxt)
        {
            //cur[now]=i;
            if((deep[e[i].v]==deep[now]+1)&&(e[i].w!=0))
            {
                int di=dfs(e[i].v,min(limit,e[i].w));
                if(di>0)
                {
                    e[i].w-=di;
                    e[i^1].w+=di;
                    return di;
                }
                
            }
        }
        return 0;
    }
    int Dinic()
    {
        int ans=0;
        while(BFS())
          while(int tmp=dfs(s,INF))
          	ans+=tmp;
        return ans;
    }
    int main()
    {
    //	freopen("1.in","r",stdin); 
        memset(head,-1,sizeof(head));
        scanf("%d%d%d",&n,&m,&es);
        s=0,t=n+m+1;
        for(register int i=1;i<=n;++i)
        {
            add(s,i,1);
            add(i,s,0);
        }
        for(register int i=1;i<=m;++i)
        {
            add(i+n,t,1);
            add(t,i+n,0);
        }
        for(register int i=1;i<=es;++i)
        {
            scanf("%d%d",&x,&y);
            if(x>n||y>m) continue;
            kx[++cnt1]=x;
            ky[cnt1]=y;
        }
        for(register int i=1;i<=cnt1;++i)
        {
            add(kx[i],ky[i]+n,1);
            add(ky[i]+n,kx[i],0);
        }
        cout<<Dinic();
        //printf("%d",maxflow);
        return 0;
    }
    
    

    时间复杂度

    匈牙利算法的时间复杂度是(O(n imes m)),但事实上,对于绝大部分的二分图,匈牙利算法都跑不够上限。
    (Dinic)的时间复杂度是(O(n imessqrt{m}))
    关于网络流的讲解,之后会更新。

  • 相关阅读:
    第六章 优化服务器设置--高性能MySQL 施瓦茨--读书笔记
    skip-external-locking --mysql配置说明
    mysql配置文件my.cnf详解
    Response.Redirect 打开新窗口的两种方法
    .net中Response.End() 和Response.Redirect("http://dotnet.aspx.cc");
    onclientclick与onclick的问题.
    a href="javascript:void(0)" 是什么意思?加不加上有什么区别?
    ashx是什么文件
    CSS里的 no-repeat
    css中 repeat-x 的简单用法
  • 原文地址:https://www.cnblogs.com/Liuz8848/p/10919737.html
Copyright © 2011-2022 走看看