利用KM算法中的l(x)+l(y)>=w(x,y)。算法结束后所有标顶之和是最小的。即所求答案。
#include <iostream> #include <cstdio> #include <cstring> #include <string> #include <cmath> #include <vector> #include <queue> #include <map> #include <algorithm> #define ll long long #define INF 2139062143 #define inf -2139062144 #define MOD 20071027 #define MAXN 505 using namespace std; int link[MAXN],gl[MAXN][MAXN],lx[MAXN],ly[MAXN],slack[MAXN]; bool visx[MAXN],visy[MAXN]; int n; bool match(int x) { visx[x]=true; for(int y=1; y<=n; ++y) if(!visy[y]) { int t=lx[x]+ly[y]-gl[x][y]; if(t==0) { visy[y]=true; if(link[y]==-1||match(link[y])) { link[y]=x; return true; } } else slack[y]=min(slack[y],t); } return false; } int KM() { memset(lx,0x80,sizeof(lx)); memset(ly,0,sizeof(ly)); memset(link,-1,sizeof(link)); for(int i=1; i<=n; ++i) for(int j=1; j<=n; ++j) lx[i]=max(lx[i],gl[i][j]); for(int x=1; x<=n; ++x) { memset(slack,0x7f,sizeof(slack)); while(1) { memset(visx,0,sizeof(visx)); memset(visy,0,sizeof(visy)); if(match(x)) break; int d=INF; for(int i=1; i<=n; ++i) if(!visy[i]&&d>slack[i]) d=slack[i]; for(int i=1; i<=n; ++i) if(visx[i]) lx[i]-=d; for(int i=1; i<=n; ++i) if(visy[i]) ly[i]+=d; else slack[i]-=d; } } int res=0; for(int i=1; i<=n; ++i) if(link[i]!=-1) res+=gl[link[i]][i]; return res; } int main() { while(scanf("%d",&n)!=EOF) { for(int i=1; i<=n; ++i) for(int j=1; j<=n; ++j) scanf("%d",&gl[i][j]); int res=KM(); for(int i=1; i<=n; ++i) if(i==1) printf("%d",lx[i]); else printf(" %d",lx[i]); printf("\n"); for(int i=1; i<=n; ++i) if(i==1) printf("%d",ly[i]); else printf(" %d",ly[i]); printf("\n"); printf("%d\n",res); } return 0; }