给定 \(n*n\) 的矩阵,现在给每行安排一个权值 \(x_i\),给每列安排一个权值 \(y_j\),
使得 \(x_i+y_j\geq a_{i,j}\),并且使 \(\sum_{i=1}^nx_i+y_i\) 最小。
学过KM算法的话,就应该知道可以将 \(x_i\) 和 \(y_i\) 当成顶标,并且当 \(x_i+y_j=a_{i,j}\) 时取得最小值,
那么就转换成二分图最大权匹配,直接跑KM算法将匹配的边权和加起来就是答案。
#include <cstdio> #include <cctype> #include <cmath> #include <queue> using namespace std; const int N=511; bool vx[N],vy[N]; queue<int>q; int slack[N],lx[N],ly[N],G[N][N]; int px[N],py[N],link[N],n,ans; int iut(){ int ans=0,f=1; char c=getchar(); while (!isdigit(c)) f=(c=='-')?-f:f,c=getchar(); while (isdigit(c)) ans=ans*10+c-48,c=getchar(); return ans*f; } void print(int ans){ if (ans>9) print(ans/10); putchar(ans%10+48); } int min(int a,int b){return a<b?a:b;} int max(int a,int b){return a>b?a:b;} void adjust(int y){ for (int _y;y;y=_y){ _y=px[link[y]]; px[link[y]]=y; py[y]=link[y]; } } void bfs(int st){ for (int i=1;i<=n;++i) slack[i]=0x3f3f3f3f,vx[i]=vy[i]=0; while (!q.empty()) q.pop(); q.push(st); while (1){ while (!q.empty()){ int x=q.front(); vx[x]=1,q.pop(); for (int y=1;y<=n;++y) if (!vy[y]&&slack[y]>lx[x]+ly[y]-G[x][y]){ slack[y]=lx[x]+ly[y]-G[x][y],link[y]=x; if (!slack[y]){ vy[y]=1; if (!py[y]) {adjust(y); return;} else q.push(py[y]); } } } int mn=0x3f3f3f3f; for (int i=1;i<=n;++i) if (!vy[i]) mn=min(mn,slack[i]); for (int i=1;i<=n;++i){ if (vx[i]) lx[i]-=mn; if (vy[i]) ly[i]+=mn; else slack[i]-=mn; } for (int i=1;i<=n;++i) if (!vy[i]&&!slack[i]){ vy[i]=1; if (!py[i]) {adjust(i); return;} else q.push(py[i]); } } } void KM(){ for (int i=1;i<=n;++i){ link[i]=ly[i]=px[i]=py[i]=0,lx[i]=-0x3f3f3f3f; for (int j=1;j<=n;++j) lx[i]=max(lx[i],G[i][j]); } for (int i=1;i<=n;++i) bfs(i); } int main(){ while (scanf("%d",&n)==1){ for (int i=1;i<=n;++i) for (int j=1;j<=n;++j) G[i][j]=iut(); KM(),ans=0; for (int i=1;i<=n;++i) ans+=lx[i],ans+=ly[i]; for (int i=1;i<=n;++i) print(lx[i]),putchar(i==n?10:32); for (int i=1;i<=n;++i) print(ly[i]),putchar(i==n?10:32); print(ans),putchar(10); } return 0; }