• 复习2二分图匹配


    我们现在讲下二分图匹配

    1.什么是二分图

    二分图又称作二部图,是图论中的一种特殊模型。 设(G=(V,E))是一个无向图,如果顶点(V)可分割为两个互不相交的子集((A,B)),并且图中的每条边((i,j))所关联的两个顶点i和j分别属于这两个不同的顶点集((iin A,jin B)),则称图(G)为一个二分图。

    2.二分图的问题

    我们来想一个比较浅显的问题,我们有(n)个男孩,(m)个女孩。每个男孩只愿意和一些他指定的女孩在一起,每个女孩也只希望和她指定的一些男孩在一起。当然同性肯定不能在一起(不就是lily和gay吗)当然肯定需要双方都同意才能在一起啊【手动滑稽】,此时我们现在想知道最多有多少对能成?

    3.最佳二分图匹配(dinic)

    我们可以用一个网络流算法来解这个问题
    这里我们只需要建一个这样图,样例如下
    输入格式:
    第1行:n,m
    第2~n+1行,第i行先是一个(B_i),然后是(B_i)个数,第j个数(x_j)表示第i个男生愿意和第(x_j)个女生在一起
    第n+2~n+m+2行,第i行先是一个(G_i),然后是(G_i)个数,第j个数(y_j)表示第i个男生愿意和第(y_j)个女生在一起
    输出格式:
    一个整数表示最多的配对对数
    样例输入:

    3 3
    1 1
    2 1 2
    3 1 2 3
    2 1 2
    2 1 3
    1 3

    样例输出:

    2

    这里写图片描述

    现在我们就可以跑最大流了,最大流就是我们需要的答案

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    
    #define Maxn 1000001
    #define INF 0x7fffffff
    
    using namespace std;
    
    bool vis[1001][1001];
    
    class Dinic{
        private:
            int n,s,t,w[Maxn],v[Maxn],next[Maxn],head[Maxn],cnt;
            int depth[Maxn];
            void add_edge(int x,int y,int z){
                next[++cnt]=head[x];
                v[cnt]=y;
                w[cnt]=z;
                head[x]=cnt;
                return;
            }
        public:
            void init(int x,int y,int z){
                n=x,s=y,t=z;
                cnt=-1;
                memset(head,-1,sizeof(head));
                memset(next,-1,sizeof(next));
                return;
            }
            void addedge(int x,int y,int z){
                add_edge(x,y,z);
                add_edge(y,x,0);
                return;
            }
            bool bfs(){
                queue<int>q;
                memset(depth,0,sizeof(depth));
                depth[s]=1;
                q.push(s);
                do{
                    int u=q.front();
                    q.pop();
                    for(int i=head[u];~i;i=next[i]){
                        if(w[i]>0 and !depth[v[i]]){
                            depth[v[i]]=depth[u]+1;
                            q.push(v[i]);
                        }
                    }
                }while(!q.empty());
                return depth[t];
            }
            int dfs(int u,int now){
                if(u==t)
                    return now;
                for(int i=head[u];~i;i=next[i]){
                    if(w[i]>0 and depth[v[i]]==depth[u]+1){
                        int d=dfs(v[i],min(now,w[i]));
                        if(d>0){
                            w[i]-=d;w[i^1]+=d;
                            return d;
                        }
                    }
                }
                return 0;
            }
            int dinic(){
                int ans=0;
                while(bfs()){
                        ans+=dfs(s,INF);
                }
                return ans;
            }
    }dinic;
    int n,m,s,t;
    
    
    int main(){
        scanf("%d%d",&n,&m);
        dinic.init(n,1,n+m+2); 
        for(int i=1;i<=n;i++){
        	int x;
        	scanf("%d",&x);
        	for(int j=1;j<=x;j++){
        		int y;
        		scanf("%d",&y);
        		vis[i][y]=1;
    		}
    		dinic.addedge(1,i+1,1);
    	}
        for(int i=1;i<=m;i++){
        	int x;
        	scanf("%d",&x);
        	for(int j=1;j<=x;j++){
        		int y;
        		scanf("%d",&y);
        		if(vis[y][i]){
        			dinic.addedge(y+1,n+i+1,1);
    			}
    		}
    		dinic.addedge(n+i+1,n+m+2,1);
    	}
        printf("%d
    ",dinic.dinic());
        return 0;
    }
    

    4.我们再来看看匈牙利

    匈牙利算法(Hungarian method)是由匈牙利数学家Edmonds于1965年提出,因而得名。匈牙利算法是基于Hall定理中充分性证明的思想,它是二分图匹配最常见的算法,该算法的核心就是寻找增广路径,它是一种用增广路径求二分图最大匹配的算法。

    复杂度

    时间复杂度邻接矩阵:最坏为(O(n^3)) 邻接表:(O(nm))
    空间复杂度 邻接矩阵: (O(n^2)) 邻接表: (O(n+m))

    思路

    这就是匈牙利算法的流程,其中找妹子是个递归的过程,最最关键的字就是“腾”字

    其原则大概是:有机会上,没机会创造机会也要上

    代码

    这是luogu模板P3386

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    
    using namespace std;
    bool f[1001][1001],vis[1001];
    int n,m,e,ans,mat[1001]; 
    bool dfs(int x){
        for(int i=1;i<=m;i++){
            if(f[x][i] and !vis[i]){
                vis[i]=1;
                if(!mat[i]){
                    mat[i]=x;
                    return 1;
                }else{
                    if(dfs(mat[i])){
                        mat[i]=x;
                        return 1;
                    }
                }
            }
        }
        return 0;
    }
    
    int main(){
        scanf("%d%d%d",&n,&m,&e);
        for(int i=1;i<=e;i++){
            int x,y;
            scanf("%d%d",&x,&y);
            if(x<=n and y<=m)f[x][y]=1;
        }
        for(int i=1;i<=n;i++){
            memset(vis,0,sizeof(vis));
            if(dfs(i))ans++;
        }
        printf("%d
    ",ans);
        return 0;
    }
    
  • 相关阅读:
    DES加密和解密PHP,Java,ObjectC统一的方法
    TreeSaver 使用教程整理——Step 3: Creating Grids
    android面试题目汇总持续更新
    Android SQLiteHelper
    Android计算地图上两点距离
    asp.net 下载文件几种方式
    android 权限
    ORA01795的原因及解决办法
    C# winFrom 通过注册新协议实现网页链接打开本地程序
    Android 读SIM卡信息
  • 原文地址:https://www.cnblogs.com/ezoihy/p/9368031.html
Copyright © 2020-2023  润新知