首页 > 代码库 > HDU 2255 奔小康赚大钱 KM算法题解

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\n",KM());  
	}  
	return 0; 
}



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