#include <bits/stdc++.h>
using namespace std;
#define ll long long
//算法使用
//w[maxn][maxn] 数组表示图的邻接矩阵
//pop表示二分图的左右两边的点的数量
//点从0到n-1
//
const int maxn = 50;
const int inf = 0x3f3f3f3f;
int w[maxn][maxn], x[maxn], y[maxn];
int pre_x[maxn], pre_y[maxn], son_y[maxn], slack[maxn], par[maxn];
int lx, ly, pop;
void adjust(int v)
{
son_y[v] = pre_y[v];
if (pre_x[son_y[v]] != -2)
{
adjust(pre_x[son_y[v]]);
}
}
bool find(int v)
{
//遍历所有没有被匹配的y点寻找可能的增广路
for (int i = 0; i < pop; i++)
{
if (pre_y[i] == -1)
{
//对于无法匹配的遍,记录一下“最少降低多少x的期望就可以和该y匹配”,该值就是slack
//假设不计算slack,那么每次减一并不能保证减小后可以获得新的增广路
if (slack[i] > x[v] + y[i] - w[v][i])
{
slack[i] = x[v] + y[i] - w[v][i];
par[i] = v;
}
//说明可以向这个方向深搜
if (x[v] + y[i] == w[v][i])
{
pre_y[i] = v;
//对面的yi点没有匹配边
if (son_y[i] == -1)
{
adjust(i);
return true;
}
//如果yi对于的匹配边另一端的xson节点已经出现在了增广路上,这条增广路不可用,去找下一个y节点
if (pre_x[son_y[i]] != -1)
{
continue;
}
//如果yi对于的匹配边另一端的xson节点没有出现在增广路上,下一个可以去跑xson节点,同时把xson节点指向yi节点
pre_x[son_y[i]] = i;
//建立了xv-yi-xson的增广后,去查询xson的循环
if (find(son_y[i]))
return true;
}
}
}
return false;
}
ll KM()
{
//初始化
for (int i = 0; i < pop; i++)
{
son_y[i] = -1;
y[i] = 0;
}
//给每个i节点寻找期望x[i]
for (int i = 0; i < pop; i++)
{
x[i] = 0;
for (int j = 0; j < pop; j++)
{
x[i] = max(x[i], w[i][j]);
}
}
bool flag;
//pop遍循环,每次循环为xi节点寻找对应的匹配
for (int i = 0; i < pop; i++)
{
//pop遍循环,对某些内容进行初始化,pre_x和pre_y是用来记录当前的增广路径的,分别指向在增广路径中该点之前的一个节点
for (int j = 0; j < pop; j++)
{
pre_x[j] = pre_y[j] = -1;
slack[j] = inf;
}
//设置xi节点为起点
pre_x[i] = -2;
//跑dfs找增广路
if (find(i))
continue;
//找不到增广路,所以需要修改期望
flag = false;
while (!flag)
{
int m = inf;
//slack[i]表示对于点yi,在尝试拓展但却因为期望和原因不能拓展的点当中,为了能够匹配,需要“期望降低值”的最小值
//slack[i]==INF说明没有某个点尝试和yi连接并失败
//关键,i需要尝试连边过,且被拒绝了
//在多有yi中去一个slack值最小的,取更新x和y的期望,就可以让下一次寻找增广路出现新的增广路
for (int j = 0; j < pop; j++)
if (pre_y[j] == -1)
m = min(m, slack[j]);
//根据是否在“失败”的增广路径上进行操作
for (int j = 0; j < pop; j++)
{
if (pre_x[j] != -1)
x[j] -= m;
if (pre_y[j] != -1)
y[j] += m;
//对于没有在增广路上出现过的点yi,因为x都降低了m,那么对应的“期望降低值”就被完成了一部分,所以去掉一个m
//对于出现在增广路上的点,不是slack数组的具体服务对象
else
slack[j] -= m;
}
//循环找到slack降低m后为0的值,这些值就是在上一次find中一次都没有被匹配到的节点
//没有降到0的就不用跑了
for (int j = 0; j < pop; j++)
{
if (pre_y[j] == -1 && !slack[j])
{
pre_y[j] = par[j];
if (son_y[j] == -1)
{
adjust(j);
flag = true;
break;
}
pre_x[son_y[j]] = j;
if (find(son_y[j]))
{
flag = true;
break;
}
}
}
}
}
ll ans = 0;
for (int i = 0; i < pop; i++)
{
ans += w[son_y[i]][i];
}
return ans;
}