HDU-2255 奔小康赚大钱 (二分图-KM)

题目链接

题意:给出房子和买家的数量都为n,还有不同房子买家愿意出的钱(以矩阵形式给出)

题解:直接输入到mp[i][j],跑一遍KM算法即可~时间复杂度为O(n^3).

代码如下:

#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<algorithm>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
const int maxn = 310;
int link[maxn], lx[maxn], ly[maxn], slack[maxn];    //lx,ly分别为x点集y点集的个数
int visx[maxn], visy[maxn], mp[maxn][maxn];
int n;
int dfs(int x) {
	visx[x] = 1;
	for (int y = 1; y <= n; y++) {
		if (visy[y]) continue;
		int t = lx[x] + ly[y] - mp[x][y];
		if (t == 0) {
			visy[y] = 1;
			if (link[y] == -1 || dfs(link[y])) {
				link[y] = x;
				return 1;
			}
		}
		else if (slack[y] > t)//不在相等子图中stack取最小的
			slack[y] = t;
	}
	return 0;
}
int km() {
	int i, j;
	memset(link, -1, sizeof(link));
	memset(ly, 0, sizeof(ly));
	for (i = 1; i <= n; i++)  //lx初始化为与它关联变中最大的
		for (j = 1, lx[i] = -inf; j <= n; j++)
			if (mp[i][j] > lx[i])	lx[i] = mp[i][j];
	for (int x = 1; x <= n; x++) {
		memset(slack, inf, sizeof(slack));
		while (1) {
			memset(visx, 0, sizeof(visx));
			memset(visy, 0, sizeof(visy));
			if (dfs(x)) //若成功,则该点增广路完成,进入下一个点的增广
				break;
			//若失败。则需要改变一些点的标号,使得图中可行边的数量增加
			//方法:将所有的增广路中的x方的点的标号全部减去一个常数d
			//      所有在增广路中的y方的点标号全部加上一个常数d
			int d = inf;
			for (int i = 1; i <= n; i++)
				if (!visy[i] && d > slack[i])
					d = slack[i];
			for (i = 1; i <= n; i++)
				if (visx[i])
					lx[i] -= d;
			for (i = 1; i <= n; i++)//修改顶标后,要把所有不在交错树中的y顶点的slack值都减去d
				if (visy[i])
					ly[i] += d;
				else
					slack[i] -= d;
		}
	}
	
	int res = 0;
	for (i = 1; i <= n; i++)
		if (link[i] > -1)
			res += mp[link[i]][i];
	return res;
}
int main() {
	while (~scanf("%d", &n)) {
		for (int i = 1; i <= n; i++)for (int j = 1; j <= n; j++)scanf("%d", &mp[i][j]);
		printf("%d\n", km());
	}
	return 0;
}


猜你喜欢

转载自blog.csdn.net/weixin_41156591/article/details/81837596