【洛谷6577】【模板】二分图最大权完美匹配
- 给定一张二分图,两边各有\(n\)个点,共有\(m\)条边,求这张二分图的最大权完美匹配。
- \(n\le500,m\le\frac{n(n-1)}2\)
\(KM\)算法
这个算法的核心思想就是给每个点一个顶标(两边点都有),满足\(a_i+b_j\ge w_{i,j}\)。
然后对于一条满足\(a_i+b_j=w_{i,j}\)的边,定义它为一条相等边。由相等边构成的子图称作相等子图。
而\(KM\)算法建立在一个基本结论上:当每个相等子图完备匹配时,二分图得到最大匹配。
其实很容易就可以找到一个可行的顶标分配方案,因此可以找到一种顶标的分配,然后在找增广路的时候同时调整,并在发现相等子图的完备匹配时立刻匹配。
具体流程就是先分配可行顶标,然后匈牙利算法去寻找增广路,找不到的时候就调整顶标,直至找到为止。
这种做法很好写,但会被卡成\(O(n^4)\),所以需要优化。
从\(DFS\)到\(BFS\)的优化
本质其实不变。
就是考虑到每次调整其实变动很小,重新\(DFS\)会造成大量冗余。
因此我们改用\(BFS\),一般情况下能够做到\(O(n^3)\),但据说还是有可能被卡成\(O(n^4)\)的(该有多无聊的出题人才会卡这玩意啊)。
代码:\(O(n^3)\)
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 500
#define INF 1e9
using namespace std;
int n,m,ex[N+5],ey[N+5],a[N+5][N+5];
int s[N+5],p[N+5],vis[N+5],px[N+5],py[N+5],sl[N+5];I void Match(CI id)//BFS寻找匹配
{
RI i,x,v,o;LL d;for(i=1;i<=n;++i) p[i]=0,sl[i]=INF;//初始化
s[v=0]=id;W(s[v])//不断搜索
{
for(x=s[v],d=INF,vis[v]=i=1;i<=n;++i) !vis[i]&&//如果没有访问过
(sl[i]>px[x]+py[i]-a[x][i]&&(sl[i]=px[x]+py[i]-a[x][i],p[i]=v),sl[i]<d&&(d=sl[o=i]));//更新,p[i]记录前驱
for(i=0;i<=n;++i) vis[i]?(px[s[i]]-=d,py[i]+=d):sl[i]-=d;v=o;//利用最小的差值更新被访问到的点的信息
}
W(v) s[v]=s[p[v]],v=p[v];//更新一遍
}
int main()
{
RI i,j;for(scanf("%d%d",&n,&m),i=1;i<=n;++i) for(j=1;j<=n;++j) a[i][j]=-INF;//初始所有边权是-INF
RI x,y,z;for(i=1;i<=m;++i) scanf("%d%d%d",&x,&y,&z),a[x][y]=max(a[x][y],z);//记录边信息
for(i=1;i<=n;++i) {for(j=1;j<=n;++j) vis[j]=0;Match(i);}//KM算法
LL t=0;for(i=1;i<=n;++i) t+=a[s[i]][i];for(printf("%lld\n",t),i=1;i<=n;++i) printf("%d ",s[i]);return 0;//输出答案并给出方案
}
待到再迷茫时回头望,所有脚印会发出光芒