#include<bits/stdc++.h>
using namespace std;
const int maxn=1e3,INF=1e9;
int W[maxn][maxn],n,m;
int Lx[maxn],Ly[maxn];//顶标
int Left[maxn];//右边第i个点对应的左边的点的编号
bool S[maxn],T[maxn];//是否在增广路
bool match(int i){
S[i]=true;
for(int j=1;j<=n;j++)if(Lx[i]+Ly[j]==W[i][j] && !T[j]){//i到j可行 且 j未被访问
T[j]=true;
if(!Left[j] || match(Left[j])){ //j未标记 或者 通过j可以找打增广路
Left[j]=i;return true;
}
}
return false;
}
void update(){//更新顶标
int a=INF;
for(int i=1;i<=n;i++)if(S[i]){
for(int j=1;j<=n;j++)
if(!T[j])
a=min(a,Lx[i]+Ly[j]-W[i][j]);//i在增广路 且 j不在增广路中
}
for(int i=1;i<=n;i++){
if(S[i])Lx[i]-=a;
if(T[i])Ly[i]+=a;
}
}
void KM(){
for(int i=1;i<=n;i++){
Left[i]=Lx[i]=Ly[i]=0;
for(int j=1;j<=n;j++)Lx[i]=max(Lx[i],W[i][j]);
}
for(int i=1;i<=n;i++){
for(;;){
for(int j=1;j<=n;j++)
S[j]=T[j]=0;
if(match(i))
break;
else update();
}
}
}
int main(){
scanf("%d%d",&n,&m);
/*
//针对最小权匹配
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
W[i][j]=-1e9;
*/
while(m--)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
/*
//针对最大权匹配
W[u][v]=w;
*/
/*
//针对最小权匹配
if(W[u][v]==-1e9)
W[u][v]=-w;
else W[u][v]=max(W[u][v],-w);
*/
}
KM();
int sum=0;
for(int i=1;i<=n;i++)sum+=W[Left[i]][i];
/*
//针对最大权匹配
printf("%d\n",sum);
*/
/*
//针对最小权匹配
printf("%d\n",-sum);
*/
return 0;
}
二分图最大权/最小权匹配
最新推荐文章于 2021-03-22 13:36:04 发布