您的位置:首页 > 其它

HDU-2255 奔小康赚大钱

2012-01-02 16:17 330 查看
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2255

解题思路:

二分图最优匹配的裸题,需要学习一下KM算法。。这道题也可以用网络流做,等学了网络流之后再写一下网络流的解题思路。

本题提供2个版本,一个是最朴素的KM算法,一个是优化后的KM算法(另外使用了输入外挂,成功刷入杭电前三



代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<climits>
#include<algorithm>
using namespace std;
#define N 310
int map

;
bool visitx
, visity
;
int lx
, ly
;
int match
;
int n;

bool Hungary(int u) //匈牙利算法
{
	visitx[u] = true;
	for(int i = 0; i < n; ++i)
	{
		if(!visity[i] && lx[u] + ly[i] == map[u][i])
		{
			visity[i] = true;
			if(match[i] == -1 || Hungary(match[i]))
			{
				match[i] = u;
				return true;
			}
		}
	}
	return false;
}

void KM_perfect_match()
{
	int temp;
	memset(lx, 0, sizeof(lx)); //初始化顶标
	memset(ly, 0, sizeof(ly)); //ly[i]为0
	for(int i = 0; i < n; ++i) //lx[i]为权值最大的边
		for(int j = 0; j < n; ++j)
			lx[i] = max(lx[i], map[i][j]);
	for(int i = 0; i < n; ++i) //对n个点匹配
	{
		while(1)
		{
			memset(visitx, false, sizeof(visitx));
			memset(visity, false, sizeof(visity));
			if(Hungary(i)) //匹配成功
				break;
			else //匹配失败,找最小值
			{
				temp = INT_MAX;
				for(int j = 0; j < n; ++j) //x在交错树中
					if(visitx[j])
						for(int k = 0; k < n; ++k) //y在交错树外
							if(!visity[k] && temp > lx[j] + ly[k] - map[j][k])
								temp = lx[j] + ly[k] - map[j][k];
				for(int j = 0; j < n; ++j) //更新顶标
				{
					if(visitx[j])
						lx[j] -= temp;
					if(visity[j])
						ly[j] += temp;
				}
			}
		}
	}
}

int main()
{
	int ans;
	while(scanf("%d", &n) != EOF)
	{
		ans = 0;
		memset(match, -1, sizeof(match));
		for(int i = 0; i < n; ++i)
			for(int j = 0; j < n; ++j)
				scanf("%d", &map[i][j]);
		KM_perfect_match();
		for(int i = 0; i < n; ++i) //权值相加
			ans += map[match[i]][i];
		printf("%d\n", ans);
	}
	return 0;
}


优化版本:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<climits>
#include<algorithm>
using namespace std;
#define N 310
int map

;
bool visitx
, visity
;
int lx
, ly
;
int slack
;
int match
;
int n;

int Scan()
{
	int res = 0 , ch ;
	while( !( ( ch = getchar() ) >= '0' && ch <= '9' ) )
	{
		if( ch == EOF )  return 1 << 30 ;
	}
	res = ch - '0' ;
	while( ( ch = getchar() ) >= '0' && ch <= '9' )
		res = res * 10 + ( ch - '0' ) ;
	return res ;
}

bool Hungary(int u) //匈牙利算法
{
	visitx[u] = true;
	for(int i = 0; i < n; ++i)
	{
		if(visity[i])
			continue;
		if(lx[u] + ly[i] == map[u][i])
		{
			visity[i] = true;
			if(match[i] == -1 || Hungary(match[i]))
			{
				match[i] = u;
				return true;
			}
		}
		else //不在相等子图
			slack[i] = min(slack[i], lx[u] + ly[i] - map[u][i]);
	}
	return false;
}

void KM_perfect_match()
{
	int temp;
	memset(lx, 0, sizeof(lx)); //初始化顶标
	memset(ly, 0, sizeof(ly)); //ly[i]为0
	for(int i = 0; i < n; ++i) //lx[i]为权值最大的边
		for(int j = 0; j < n; ++j)
			lx[i] = max(lx[i], map[i][j]);
	for(int i = 0; i < n; ++i) //对n个点匹配
	{
		for(int j = 0; j < n; ++j)
			slack[j] = INT_MAX;
		while(1)
		{
			memset(visitx, false, sizeof(visitx));
			memset(visity, false, sizeof(visity));
			if(Hungary(i)) //匹配成功
				break;
			else //匹配失败,找最小值
			{
				temp = INT_MAX;
				for(int j = 0; j < n; ++j)
					if(!visity[j])
						if(temp > slack[j])
							temp = slack[j];
				for(int j = 0; j < n; ++j) //更新顶标
				{
					if(visitx[j])
						lx[j] -= temp;
					if(visity[j])
						ly[j] += temp;
					else
						slack[j] -= temp;
				}
			}
		}
	}
}

int main()
{
	int ans;
	while(scanf("%d", &n) != EOF)
	{
		ans = 0;
		memset(match, -1, sizeof(match));
		for(int i = 0; i < n; ++i)
			for(int j = 0; j < n; ++j)
				map[i][j] = Scan();
		KM_perfect_match();
		for(int i = 0; i < n; ++i) //权值相加
			ans += map[match[i]][i];
		printf("%d\n", ans);
	}
	return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: