oi不需要证明,确实因为时间有限,还有脑力有限,咳咳~就直接给结论和算法流程了
二分图有两类点,这里称X类,和Y类。

  • 顶标:exey,分别表示x类点和y类点的顶标。此算法全程任意边(u,v)满足ex(u)+ex(v)len(u,v)

  • 相等子图:由满足ex(u)+ey(v)=len(u,v)的边构成的子图。

  • 性质:相同子图存在原图的完美匹配,则一定是最大完美匹配

  • 目标:满足 ex(u)+ex(v)len(u,v) 的情况下调整顶标,加入边到相等子图中找增广路,使得得到原图的完美匹配。

  • 算法流程:我们在匈牙利算法找增广路的背景下设计该算法。
    1.从X中每个点出发,找增广路。
    2.找到增广路则结束
    3.否则,通过改顶标加入一边。回到2
    流程很简单,不过怎么改顶标。
    首先明确要找的边从X已经遍历到的点连向Y还未遍历到的点。
    因此可以将遍历到的边 ex(u)=Δex(v)+=Δ,这样遍历到的边不会改变但满足上面的边会减少Δ
    综上加入此类边中ex(u)+ex(v)len(u,v)最小的边即可。
    而查询最小O(n2)可以用slack优化至O(n)slack维护Y中未遍历到的每个点到所有X中遍历到的点的min(ex(u)+ex(v)len(u,v))
    每次加边导致X中有新点被遍历,直接O(n)更新,查询slack扫一遍找新边也是O(n)的。
    流程结合代码看一下就好了,注意改顶标时别漏了起点(从0开始遍历)。
    由于每次是加入一条边,所以直接用bfs迭代的思想即可(写的也不像bfs)。
    看代码显然:O(n3)

  • code

点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll inf = 1e18;
const int N = 505;

int pre[N], link[N], n;
ll ex[N], ey[N], mp[N][N], slack[N];
bool visy[N];

void KM() {
	for(int s = 1; s <= n; s++) {
		for(int i = 1; i <= n; i++) {slack[i] = inf; pre[i] = 0; visy[i] = 0;}
		int x, y = 0; link[y] = s;
		while(1) {
			x = link[y]; visy[y] = 1;
			ll dta = inf, ny;
			for(int i = 1; i <= n; i++) {
				if(visy[i]) continue;
				ll w = ex[x] + ey[i] - mp[x][i];
				if(slack[i] > w) {slack[i] = w; pre[i] = y;}
				if(dta > slack[i]) {dta = slack[i]; ny = i;}
			}
			for(int i = 0; i <= n; i++) {		//link0=s 
				if(visy[i]) {ex[link[i]] -= dta; ey[i] += dta;}
				else {slack[i] -= dta;} 
			}
			y = ny;
			if(!link[y]) {break;}
		}
		while(y) {link[y] = link[pre[y]]; y = pre[y];}
	}
	ll res = 0;
	for(int i = 1; i <= n; i++) res += mp[link[i]][i];
	printf("%lld\n", res);
	for(int i = 1; i <= n; i++) {printf("%d ", link[i]);}
}

int main() {
	int m; scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; i++) {for(int j = 1; j <= n; j++) mp[i][j] = -inf; ex[i] = -inf;}
	for(int i = 1; i <= m; i++) {
		int u, v; ll w; scanf("%d%d%lld", &u, &v, &w);
		mp[u][v] = max(mp[u][v], w);
		ex[u] = max(ex[u], w);
	}
	KM();
	return 0;
}
  • 非完美匹配
    • 首先保证|X||Y|
    • 一定要是完美匹配,首先|X|=|Y|,虚边设infX中每个点出发如果min到的为inf,也就是无边那就无解。
    • 最大匹配就行:虚边都是0