基本概念
二分图有两个种点:X和Y。X与Y之间存在一些边,每个边有一个权值。现要求求一组X与Y间的通过边实现的一一匹配,使得得到的边权和最大。
总体过程
对每个X节点设置一个顶标Xl,初值为与X相邻的边的权值最大值;Y节点设置一个顶标Yl,初值为0。当前情况下,如果Xl[x]+Yl[x]==weight[x][y],则此时的边(x,y)为可匹配边。weight[x][y]越接近Xl[x]+Yl[y],则边(x,y)越有可能为匹配边。
枚举每一个X,对以下步骤循环:对于当前图所有可匹配的边所形成的子图用匈牙利算法进行交错路径搜索。搜索到了交错路径那皆大欢喜,把交错路径上的匹配边翻转一下,重新设置与y节点匹配的x,便完成了对当前X的匹配;否则,更改原有的匹配方式,使得边权和变小的程度尽量小,且匹配的边数不变,再接着找交错路径……。此过程一直持续到寻找到交错路径为止。
如何更改原有的匹配方式?
利用匈牙利算法中访问的节点标记,在访问到的X节点和未访问到的Y节点中找到一对x和y,使得delta=xl[x]+yl[y]-weight[x][y]最小,然后将Vis标记过的Xl[x]-=delta, Yl[y]+=delta,此时Xl[x]+Yl[y]==weight[x][y],相当于将xy匹配,并将原先与y相连的x'节点断开。其他边不变。这样匹配边的总数不变。
对于delta,我们可以再在FindPath中把所有x节点的delta最小值预先算出来。
注意事项
- Xl[x]+Yl[y]>=XYweight[x][y]。
- 最后统计边权和时,按照YmatchX搜索,而不是通过可匹配边(Xl[x]+Yl[y]==weight[x][y])搜索。
- X也有Vis,求交错路径时不要忘了设置X的Vis。
- FindPath算Delta时,else后的内容应当放在if(Xl[x]+Yl[y]==XYweight[x][y])下,而不是if(!Yvis[y])因为delta的定义是vis过的x与没有vis过的y的最小delta值
- 设置Yvis=true应当放在if(Xl[x]+Yl[y]==XYweight[x][y])内,而不是其外部、if(!Yvis[y])内。因为只有Xl[x]+Yl[y]==XYweight[x][y]时边(x,y)才是匹配边。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std; const int MAX_X = 1010, MAX_Y = 1010, INF = 0x3f3f3f3f;
#define LOOP(i, n) for(int i=1; i<=n; i++) struct KM
{
int XYweight[MAX_X][MAX_Y];
int YmatchX[MAX_Y];
int Xl[MAX_X], Yl[MAX_Y];
int Delta[MAX_X];
bool Yvis[MAX_Y], Xvis[MAX_X];
int totX, totY; bool FindPath(int x)
{
Xvis[x] = true;
LOOP(y, totY)
{
if (!Yvis[y])
{
if (Xl[x] + Yl[y] == XYweight[x][y])
{
Yvis[y] = true;
if (!YmatchX[y] || FindPath(YmatchX[y]))
{
YmatchX[y] = x;
return true;
}
}
else
Delta[x] = min(Delta[x], Xl[x] + Yl[y] - XYweight[x][y]);
}
}
return false;
} int Proceed()
{
memset(YmatchX, 0, sizeof(YmatchX));
memset(Xl, 0, sizeof(Xl));
memset(Yl, 0, sizeof(Yl));
LOOP(x, totX)
LOOP(y, totY)
Xl[x] = max(Xl[x], XYweight[x][y]);
LOOP(firstX, totX)
{
while (true)
{
memset(Xvis, false, sizeof(Xvis));
memset(Yvis, false, sizeof(Yvis));
memset(Delta, INF, sizeof(Delta));
if (FindPath(firstX))
break;
int delta = INF;
LOOP(x, totX)
if (Xvis[x])
delta = min(delta, Delta[x]);
if (delta == INF)
break;
LOOP(x, totX)
if (Xvis[x])
Xl[x] -= delta;
LOOP(y, totY)
if (Yvis[y])
Yl[y] += delta;
}
}
int ans = 0;
LOOP(y, totY)
if (YmatchX[y])
ans += XYweight[YmatchX[y]][y];
return ans;
}
}g; int main()
{
int tot;
while (~scanf("%d", &tot))
{
g.totX = tot;
g.totY = tot;
LOOP(x, tot)
{
LOOP(y, tot)
{
int w;
scanf("%d", &w);
g.XYweight[x][y] = w;
}
}
printf("%d\n", g.Proceed());
}
return 0;
}