ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

【luogu P6577】【模板】二分图最大权完美匹配(KM算法)

2022-02-28 08:32:44  阅读:215  来源: 互联网

标签:匹配 slack int luogu KM P6577 ex ey 501


【模板】二分图最大权完美匹配

题目链接:luogu P6577

题目大意

一个二分图,有一些带权边,保证有完美匹配。
求一种最大匹配的方案使得匹配边的边权和最大。

思路

KM 算法的模板题。

它有一定的针对性:一定要是带权的完美匹配。
然后我们定义每个点有一个顶表(一个值)\(e_x\)。

对于每一条边 \((u,v)\),我们要满足 \(e_u+e_v\geqslant w(u,v)\)。
然后如果 \(e_u+e_v=w(u,v)\),那我们就可以把这条边放进二分图中。
那如果这个时候的图能跑出完美匹配,那此时的边权和即是结果。

但是你发现定标的值不能直接确定,所以算法的流程是这样的:
确定定标的值,跑匹配判断(如果匹配是 \(n\) 就结束),否则就修改定标,然后重复操作。

然后接着问题就是一开始怎么给定标的值,而且每次怎么修改。
一开始给定标,我们可以让 \(ex_x=0,ey_x=\max\limits_{i=1}^nw(x,i)\)。
然后每次修改,我们就是要减少一些 \(e_u+e_v\) 的值使得更多边在图中。

那我们修改就是找一条边 \((i,j)\),它一个不在最大匹配,一个在。
那我们要让他加入,我们就要让他满足条件,那定标和要减少 \(d_i+d_j-w(i,j)\)。
那因为 \(j\) 已经在最大匹配中了,所以我们就直接把二分图最大匹配中的任意点 \(i\) 都把 \(ex_i+d\) 或者把 \(ey_i-d\)。

那我们为了满足 \(e_u+e_v\geqslant w(u,v)\),所以我们要每次的 \(d\) 尽量小。
那每次找边复杂度 \(O(n^2)\),二分图匹配的复杂度是 \(O(n^2)\),总的复杂度为 \(O(n^4)\)。

然后发现每次都暴力找 \(d\) 太慢了,我们考虑用一个数组 \(slack_j\) 表示 \(ex_i+ey_i-w(i,j)\) 的最小值,然后在跑增广路的时候修改即可做到 \(O(n^3)\)。

吗?
其实会假。因为如果你匹配的部分跑到 \(O(n^2)\),它还是 \(O(n^4)\)。
然后我们发现我们每次只是修改了一条边,所以我们匹配的时候有一部分是跟原来一样的。
然后我们把 dfs 改成 bfs,就可以真正的变成 \(O(n^3)\)。

代码

\(O(n^4)\) 版

#include<cstdio>
#include<iostream>
#define ll long long
#define INF 0x3f3f3f3f3f3f3f3f

using namespace std;

int n, m, x, y, matched[501];
ll dis[501][501], ex[501], ey[501], slack[501], w;
bool inx[501], iny[501];

bool match(int now) {
	iny[now] = 1;
	for (int i = 1; i <= n; i++) {
		if (inx[i]) continue;
		ll g = ex[i] + ey[now] - dis[now][i];
		if (!g) {
			inx[i] = 1;
			if (!matched[i] || match(matched[i])) {
				matched[i] = now; return 1;
			}
		}
		else slack[i] = min(slack[i], g);
	}
	return 0;
}

ll KM() {
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= n; j++) slack[j] = INF;
		while (1) {
			for (int j = 1; j <= n; j++) inx[j] = iny[j] = 0;
			if (match(i)) break;
			ll d = INF;
			for (int j = 1; j <= n; j++) {
				if (!inx[j]) d = min(d, slack[j]);
			}
			for (int j = 1; j <= n; j++) {
				if (iny[j]) ey[j] -= d;
				if (inx[j]) ex[j] += d;
					else slack[j] -= d;
			}
		}
	}
	ll re = 0;
	for (int i = 1; i <= n; i++)	
		re += dis[matched[i]][i];
	return re;
}

int main() {
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= n; i++) {
		ey[i] = -INF;
		for (int j = 1; j <= n; j++)
			dis[i][j] = -INF;
	}
	for (int i = 1; i <= m; i++) {
		scanf("%d %d", &x, &y); scanf("%lld", &w);
		dis[x][y] = max(dis[x][y], w);
		ey[x] = max(ey[x], dis[x][y]);
	}
	
	printf("%lld\n", KM());
	for (int i = 1; i <= n; i++)
		printf("%d ", matched[i]);
	
	return 0;
}

\(O(n^3)\) 版

#include<cstdio>
#include<cstring>
#include<iostream>
#define ll long long
#define INF 0x3f3f3f3f3f3f3f3f

using namespace std;

int n, m, x, y, matched[501], bef[501];
ll dis[501][501], ex[501], ey[501], slack[501], w;
bool iny[501];

void match(int now) {
	int x, y = 0, ty = 0;
	matched[y] = now;
	while (1) {
		x = matched[y]; ll d = INF; iny[y] = 1;
		for (int i = 1; i <= n; i++) {
			if (iny[i]) continue;
			if (slack[i] > ex[x] + ey[i] - dis[x][i]) {
				slack[i] = ex[x] + ey[i] - dis[x][i];
				bef[i] = y;
			}
			if (slack[i] < d) {
				d = slack[i]; ty = i;
			}
		}
		for (int i = 0; i <= n; i++) {
			if (iny[i]) ex[matched[i]] -= d, ey[i] += d;
				else slack[i] -= d;
		}
		y = ty;
		if (!matched[y]) break;
	}
	while (y) {
		matched[y] = matched[bef[y]];
		y = bef[y];
	}
}

ll KM() {
	for (int i = 1; i <= n; i++) {
		memset(iny, 0, sizeof(iny));
		for (int j = 1; j <= n; j++) slack[j] = INF;
		memset(bef, 0, sizeof(bef));
		match(i);
	}
	ll re = 0;
	for (int i = 1; i <= n; i++)	
		if (matched[i])
			re += dis[matched[i]][i];
	return re;
}

int main() {
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= n; i++) {
		ey[i] = -INF;
		for (int j = 1; j <= n; j++)
			dis[i][j] = -INF;
	}
	for (int i = 1; i <= m; i++) {
		scanf("%d %d %lld", &x, &y, &w);
		dis[x][y] = max(dis[x][y], w);
		ey[x] = max(ey[x], dis[x][y]);
	}
	
	printf("%lld\n", KM());
	for (int i = 1; i <= n; i++)
		printf("%d ", matched[i]);
	
	return 0;
}

标签:匹配,slack,int,luogu,KM,P6577,ex,ey,501
来源: https://www.cnblogs.com/Sakura-TJH/p/luogu_P6577.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有