题解:比较经典的做法,题目给的不等式row[i] + col[j] >= w[i][j]和二分图最佳完美匹配的很像,可以联想到KM算法, 把横纵坐标分别看作二分图的顶点,每个w(i,j)看作边权值,跑一遍匈牙利算法即可。
(KM算法的过程实际是最大化边权和,最小化顶标和的过程)
证明是最小化订标只需在最佳完美匹配的状态时,假设减少某点的订标引入新边,那么原来与此点连接的另外一点的订标也要增加那么多,那订标和就减不少
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 510;
int n;
int w[maxn][maxn];
int lx[maxn],ly[maxn],S[maxn],T[maxn];
int lefts[maxn],rights[maxn];
bool dfs(int u) {
S[u] = true;
for(int i = 1;i <= n;i++) if(lx[u]+ly[i]==w[u][i]&&!T[i]){
T[i] = true;
if(lefts[i]==0||dfs(lefts[i])) {
lefts[i] = u;
rights[u] = i;
return true;
}
}
return false;
}
void up() {
int a = 1e9;
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]);
}
for(int i = 1;i <= n;i++) {
if(S[i]) lx[i] -= a;
if(T[i]) ly[i] += a;
}
}
void KM() {
memset(lefts,0,sizeof(lefts));
memset(rights,0,sizeof(rights));
for(int i = 1;i <= n;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++) {
while(true) {
memset(S,0,sizeof(S));
memset(T,0,sizeof(T));
if(dfs(i)) break; else up();
}
}
}
int main() {
while(scanf("%d",&n) == 1) {
for(int i = 1;i <= n;i++)
for(int j = 1;j <= n;j++) scanf("%d",&w[i][j]);
KM();
int t = 0;
for(int i = 1;i <= n;i++) printf("%d ",lx[i]),t+=lx[i];
printf("\n");
for(int i = 1;i <= n;i++) printf("%d ",ly[i]),t+=ly[i];
printf("\n%d\n",t);
}
return 0;
}