题意:
给出一个n * n的矩阵,要求给每一行和每一列安排一个数字,使得对于每个数字a[i][j],这一行安排的数字row[i]和这一列安排的数字col[j]满足row[i] + col[j] >= a[i][j]。要求使得每一行安排的数字和每一列安排的数字之和最小。输出每一行和每一列安排的数字,再输出最小值。
思路:
对于row[i] + col[j] >= a[i][j]这个条件,可以联想到KM算法中的一个式子lx[i] + ly[j] >= w[i][j],于是就可以把每一行安排的值视为左顶标,每一列安排的值视为右顶标,然后进行一次KM算法求最大匹配,那么得到的左右顶标之和就是最小的,即每一行安排的数字和每一列安排的数字之和最小。
有两个地方比较难想:
第一是求的是最小值,但是求的却是最大匹配,不是最小匹配,因为我们求的最后结果是满足lx[i] + ly[j] >= w[i][j]的,但是最小匹配满足的条件是lx[i] + ly[j] >= -w[i][j],这不满足条件。
第二是如何证明求出来的是最小值,因为求出的匹配是一个导出子图,而导出子图的每一条边都满足lx[i] + ly[j] == w[i][j],如果把这个结果减小,那么必然存在某些边有lx[i] + ly[j] < w[i][j],这样就不满足题中所给条件。
复杂度O(n^3)。
代码:
1 #include <stdio.h> 2 #include <string.h> 3 #include <algorithm> 4 using namespace std; 5 6 const int N = 505; 7 const int inf = 0x3f3f3f3f; 8 9 int lx[N],ly[N]; 10 bool vis_x[N],vis_y[N]; 11 int slack[N]; 12 int match[N]; 13 int w[N][N]; 14 15 bool dfs(int u,int n) 16 { 17 vis_x[u] = 1; 18 19 for (int i = 0;i < n;i++) 20 { 21 if (vis_y[i]) continue; 22 23 int gap = lx[u] + ly[i] - w[u][i]; 24 25 if (gap == 0) 26 { 27 vis_y[i] = 1; 28 29 if (match[i] == -1 || dfs(match[i],n)) 30 { 31 match[i] = u; 32 return true; 33 } 34 } 35 else 36 { 37 slack[i] = min(slack[i],gap); 38 } 39 } 40 41 return false; 42 } 43 44 int km(int n) 45 { 46 memset(match,-1,sizeof(match)); 47 memset(ly,0,sizeof(ly)); 48 //for (int i = 0;i < n;i++) lx[i] = -inf; 49 50 for (int i = 0;i < n;i++) 51 { 52 lx[i] = w[i][0]; 53 54 for (int j = 1;j < n;j++) 55 { 56 lx[i] = max(lx[i],w[i][j]); 57 } 58 } 59 60 for (int i = 0;i < n;i++) 61 { 62 memset(slack,inf,sizeof(slack)); 63 64 while (1) 65 { 66 memset(vis_x,0,sizeof(vis_x)); 67 memset(vis_y,0,sizeof(vis_y)); 68 69 if (dfs(i,n)) break; 70 71 int d = inf; 72 73 for (int j = 0;j < n;j++) 74 { 75 d = min(d,slack[j]); 76 } 77 78 for (int j = 0;j < n;j++) 79 { 80 if (vis_x[j]) lx[j] -= d; 81 82 if (vis_y[j]) ly[j] += d; 83 //else slack[j] -= d; 84 } 85 } 86 } 87 88 int ans = 0; 89 90 for (int i = 0;i < n;i++) 91 { 92 ans += w[match[i]][i]; 93 } 94 95 return ans; 96 } 97 98 int main() 99 { 100 int n; 101 102 while (scanf("%d",&n) != EOF) 103 { 104 for (int i = 0;i < n;i++) 105 { 106 for (int j = 0;j < n;j++) 107 { 108 int t; 109 110 scanf("%d",&t); 111 112 w[i][j] = t; 113 } 114 } 115 116 117 118 int res = km(n); 119 int ans = 0; 120 121 for (int i = 0;i < n;i++) 122 { 123 ans += (lx[i]); 124 printf("%d%c",lx[i],i == n - 1 ? ' ' : ' '); 125 } 126 127 for (int i = 0;i < n;i++) 128 { 129 ans += (ly[i]); 130 printf("%d%c",ly[i],i == n - 1 ? ' ' : ' '); 131 } 132 133 printf("%d ",res); 134 } 135 136 return 0; 137 }