• HDU 2255 奔小康赚大钱 KM算法题解


    KM算法求的是完备匹配下的最大权匹配,是Hungary算法的进一步,由于Hungary算法是最大匹配的算法,不带权。


    经典算法,想不出来的了,要參考别人的。然后消化吸收吧。

    由于真的非常复杂的算法。

    我理解算法匹配思想:
    1 開始的时候,全部边都记录自己的最优匹配,无论有没有冲突
    2 递归循环的时候。假设找不到自己的最优匹配,那么就找次要匹配
    3 次要匹配不行,继续找下一个次优匹配,全部点都一定要找到解
    难点:
    怎样记录这些信息,以便循环处理全部点。


    牵涉到非常多什么增广路,交错树之类的,名词,术语,变量就一大堆。

    心浮气躁可不能学这种算法了,要心平气和,深呼吸,然后慢慢消化才行。

    这个博客分析挺具体的。且带图:

    http://blog.csdn.net/wuxinxiaohuangdou/article/details/14056987

    http://blog.csdn.net/ZYY173533832/article/details/11519291

    两个博客一样的图,也不知道谁抄谁的了。


    我以下算法是略微改动一点而成的程序。

    主要是slack能够不使用数组记录,仅仅须要记录当前的得到的最小值就能够了。


    #include <stdio.h>
    #include <string.h>
    #include <algorithm>
    #include <limits.h>
    using namespace std;
    
    const int MAX_N = 301;
    int N;
    int link[MAX_N];//右边到左边的连线
    int slack;//当前dfs中,訪问了的点中能够让利的最小限度
    bool visLeft[MAX_N], visRight[MAX_N];
    int curMaxVal[MAX_N], giveAwayVal[MAX_N];
    int gra[MAX_N][MAX_N];
    
    bool Hungary(int u)
    {
    	visLeft[u] = true;
    	for (int v = 1; v <= N; v++)
    	{
    		if (visRight[v]) continue;
    		int curSlack = curMaxVal[u] + giveAwayVal[v] - gra[u][v];
    		if (curSlack == 0)
    		{
    			visRight[v] = true;
    			if (!link[v] || Hungary(link[v]))
    			{
    				link[v] = u;
    				return true;
    			}
    		}
    		else if(slack > curSlack) slack = curSlack;
    	}
    	return false;
    }
    
    int KM()
    {
    	memset(giveAwayVal, 0, sizeof(giveAwayVal));
    	memset(link, 0, sizeof(link));
    	for (int i = 1; i <= N; i++)
    		curMaxVal[i] = *max_element(gra[i]+1, gra[i]+N+1);
    
    	for (int i = 1; i <= N; i++)
    	{
    		while (true)
    		{
    			memset(visLeft, 0, sizeof(visLeft));
    			memset(visRight, 0, sizeof(visRight));
    			slack = INT_MAX;
    			if (Hungary(i)) break;
    
    			for (int j = 1; j <= N; j++)
    			{
    				if (visLeft[j]) curMaxVal[j] -= slack;
    				if (visRight[j]) giveAwayVal[j] += slack;
    			}
    		}
    	}
    	int ans = 0;
    	for (int i = 1; i <= N; i++) ans += gra[link[i]][i];
    	return ans;
    }
    
    int main()
    {
    	while(~scanf("%d", &N))  
    	{
    		for(int i = 1; i <= N; i++)  
    			for(int j = 1; j <= N;j++)  
    				scanf("%d",&gra[i][j]);
    
    		printf("%d
    ",KM());  
    	}  
    	return 0; 
    }



  • 相关阅读:
    Windows Phone 7 电话本相关的操作
    Windows CE 下判断 URL 地址是否有效
    cigarettes
    开灯问题
    日期计算
    小学生算术
    另一种阶乘问题
    鸡兔同笼
    笨小熊
    6174问题
  • 原文地址:https://www.cnblogs.com/mthoutai/p/6920066.html
Copyright © 2020-2023  润新知