zoukankan      html  css  js  c++  java
  • 匈牙利算法

    二分图的最大匹配:匈牙利算法

    讲之前本蒟蒻先普及一个重要专业名词

    增广路。

    如果你仔细读过并画过图,不难发现如果找到一条增广路,那么配对的个数就会加1。
    所以说,增广路的本质其实就是一条路径的起点和终点都未配对的点的边。


    匈牙利算法:

    这个叫匈牙利算法(Hungarian method)的东西是由匈牙利数学家Edmonds于1965年提出,所以叫匈牙利算法。匈牙利算法是二分图匹配最常见的算法,该算法的核心就是寻找增广路径,它是一种用增广路径求二分图最大匹配的算法。

    复杂度:

    时间复杂度 :

    邻接矩阵最坏为 $ O( n^3 ) $
    邻接表:$ O(mn) $

    空间复杂度 :

    邻接矩阵:$ O( n^2 ) $
    邻接表:$ O(n+m) $

    另一个重要概念:二分图

    二分图是图论中的一种特殊模型。 设G=(V,E)是一个无向图,如果顶点V可分割为两个互不相交的子集(A,B),并且图中的每条边(i,j)所关联的两个顶点i和j分别属于这两个不同的顶点集,则称图G为一个二分图。
    学过高中数学的话应该能看懂我在说什么(逃

    简而言之,就是顶点集V可分割为两个互不相交的子集,并且图中每条边依附的两个顶点都分属于这两个互不相交的子集,两个子集内的顶点不相邻。满足这样的图就叫二分图。

    但我们怎么判断一个图是不是二分图???

    其实也不难,用红蓝点的方法就行。首先讲任意的一个顶点染成红色,再把这个点相邻的顶点染成蓝色,如果按照这种染色方式可以将所有的顶点全部着色,并且相邻的顶点的颜色不同,那么该图就是一个二分图。

    这里贴一下代码
    #define MAXV 1000//这里应该根据题目自定
    
    vector<int> G[MAXV];  //图 
    int V;                       //顶点数 
    int color[MAXV];  //顶点的颜色 (1 or -1) 
    
    
    //顶点v,颜色c 
    bool dfs(int v,int c){
        color[v] = c;
        //把当前顶点相邻的顶点扫一遍 
        for(int i = 0;i < G[v].size(); i++){
            //如果相邻顶点已经被染成同色了,说明不是二分图 
            if(color[G[v][i]] == c) return false;
            //如果相邻顶点没有被染色,染成-c,看相邻顶点是否满足要求 
            if(color[G[v][i]] == 0 && !dfs(G[v][i],-c)) return false;
        }
        //如果都没问题,说明当前顶点能访问到的顶点可以形成二分图 
        return true;
    }
    
    void solve(){
        //可能是不连通图,所以每个顶点都要dfs一次 
        for(int i = 0;i < V; i++){
            if(color[i] == 0){
                //第一个点颜色为 1 
                if(!dfs(i,1)){
                    cout << "No" << endl;
                    return;
                }
            }
        }
    }
    

    这才是正文!!!

    既然上面本蒟蒻已经介绍完了有关二分图的知识,那下面该讲下匈牙利算法了!!!

    根据上文的描述,既然增广路的作用是“改进匹配方案”(即增加配对数),那么如果我们已经找到了一种匹配方案,不难发现如果在当前匹配方案下再也找不到任何增广路的话,那么当前匹配就是二分图的最大匹配,算法如下。

    1.首先从任意的一个未配对的点u开始,从点u的边中任意选一条边(假设这条边是从 $ u->v $ )开始配对。如果点v未配对,则配对成功,这是便找到了一条增广路。如果点v已经被配对,就去尝试“连锁反应”,如果这时尝试成功,就更新原来的配对关系。
    所以这里要用一个 $ matched[v] = u $ 。配对成功就将配对数加1,。

    2.如果刚才所选的边配对失败,那就要从点u的边中重新选一条边重新去试。直到点u
    配对成功,或尝试过点u的所有边为止。

    3.接下来就继续对剩下的未配对过的点一一进行配对,直到所有的点都已经尝试完毕,找不到新的增广路为止。

    4.输出配对数。

    CODE:

    //如果你已经读完题,请自动从int main()处开始阅读 
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    
    #define N 2010
    
    using namespace std;
    
    int n,m,e,ans;
    int vis[N][N];
    int ask[N],matched[N];
    
    inline bool found(int x){ //dfs找增广路 
        for (int i = 1 ; i <= m ; i++)
          if (vis[x][i]){
            if (ask[i]) 
    			continue;
            ask[i] = 1;
            if (!matched[i] || found(matched[i])) { 
    			matched[i] = x ; 
    			return true;
    		}
          }
        return false;
    }
    
    inline void match(){
        int cnt = 0;//cnt是计数器 
        memset(matched,0,sizeof(matched));
        for (int i = 1 ; i <= n ; i++){
          memset(ask,0,sizeof(ask));
          if (found(i)) 
    	  	cnt++;	//找到了就加1 
        }
        ans = cnt;
    }
    //从这里向下看起 
    int main(){
        scanf("%d%d%d",&n,&m,&e);//结点个数分别为n,m,边数为e
        for (int i = 1 ; i <= e ; i++){
          int x,y;
          scanf("%d%d",&x,&y);
          vis[x][y] = 1;
        }
        match();///匈牙利算法,见上 
        printf("%d 
    ",ans);
        return 0;
    }
    

    再补一个临接表的实现(就只放函数)

    bool dfs(int x) {
    	for(int i = head[x] ; i ; i = e[i].to) {
    		int v = e[i].from;
    		if(!used[v]) {
    			used[v] = 1;
    			if(!matched[v] || dfs(matched[i]) ) {
    				matched[v] = u;
    				return 1;
    			}
    		}
    	}
    	return 0;
    }
    int match() {
    	int res = 0;    
    	memset(match,-1,sizeof(match) );    
    	for(int u = 0 ; u < uN ; u++) {        
    		memset(used, -1 , sizeof(used));        
    		if(dfs(u)) res++;    
    	}    
    	return res; 
    }
    

    完结撒花(逃!!!

  • 相关阅读:
    数据库知识整理<二>
    数据库知识整理<一>
    面试经历感悟:
    JDK,J2EE,J2SE,J2ME的概念区别是什么呢?
    HTML与JSP页面的区别
    Java并发编程:深入剖析ThreadLocal
    剖析错误原理并解决Hibernate出现No TransactionManagerLookup specified!错误
    MyEclipse中jar包管理技巧
    String,StringBuffer与StringBuilder的区别??
    POJO / JavaBean / Entity Bean
  • 原文地址:https://www.cnblogs.com/Repulser/p/9740748.html
Copyright © 2011-2022 走看看