D27 二分图最大权完美匹配 KM算法
视频链接:https://www.bilibili.com/video/BV1cT411J7W5
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> using namespace std; #define LL long long #define N 510 #define INF 1e12 int n,m; int match[N];//右点匹配了哪个左点 int va[N],vb[N];//标记是否在交替路中 LL la[N],lb[N];//左顶标,右顶标 LL w[N][N],d[N];//维护更新的delta值 bool dfs(int x){ va[x]=1; //x在交替路中 for(int y=1;y<=n;y++){ if(!vb[y]){ if(la[x]+lb[y]-w[x][y]==0){//相等子图 vb[y]=1; //y在交替路中 if(!match[y]||dfs(match[y])){ match[y]=x; //配对 return 1; } } else //不是相等子图则记录最小的d[y] d[y]=min(d[y],la[x]+lb[y]-w[x][y]); } } return 0; } LL KM(){ //左顶标取i的出边的最大边权 for(int i=1;i<=n;i++) la[i]=-INF; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) la[i]=max(la[i],w[i][j]); for(int i=1;i<=n;i++) lb[i]=0; for(int i=1;i<=n;i++){ while(true){//直到左点i找到匹配 fill(va+1,va+n+1,0); fill(vb+1,vb+n+1,0); fill(d+1,d+n+1,INF); if(dfs(i))break; LL delta=INF; for(int j=1;j<=n;j++) if(!vb[j])delta=min(delta,d[j]); for(int j=1;j<=n;j++){//修改顶标 if(va[j])la[j]-=delta; if(vb[j])lb[j]+=delta; } } } LL res=0; for(int i=1;i<=n;i++)res+=w[match[i]][i]; return res; } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) w[i][j]=-INF; for(int i=1;i<=m;i++){ int x,y,z; scanf("%d%d%d",&x,&y,&z); w[x][y]=z; } printf("%lld\n",KM()); for(int i=1;i<=n;i++) printf("%d ",match[i]); return 0; }