1. 程式人生 > >Golden Tiger Claw(二分圖最佳匹配)

Golden Tiger Claw(二分圖最佳匹配)

【題意】 給一個nn的矩陣,每個格子中有正整數w[i][j],試為每行和每列分別確定一個數字row[i]col[i],使得任意格子w[i][j]<=row[i]+col[j]恆成立。先輸row,再輸出col,再輸出全部總和(總和應儘量小)(n<=500,w[i][j]<=100)

【思路】   本題與匹配無關,但可以用KM演算法解決。   KM演算法中的頂標就是保持了Lx[i]+ly[j]>=g[i][j]再求最大權和匹配的,但這個最大權和並沒有關係。我們可以將row[i]看成一個男的,col[i]看成一個女的,這樣男女的總數就相等。

  一般來說,Lx[i]或Ly[i]僅需要取該行/列中最大的那個數即可保證滿足要求,但是這樣太大了,可以通過調整來使得總和更小。而KM演算法的過程就是一個調整的過程,每一對匹配的男女的那條邊的權值就會滿足等號 w[i][j]=row[i]+col[j],至少需要一個來滿足等號,這樣才能保證row[i]+col[j]是達到最小的,即從j列看,col[j]滿足條件且最小,從i行看,row[i]滿足條件且最小。這剛好與KM演算法求最大權和一樣。

#include<bits/stdc++.h>
using namespace std;

typedef int type;

const type inf = 2e9
; const double eps = 1e-8; const int maxn = 505; int n, m; int matchx[maxn], matchy[maxn]; int visx[maxn], visy[maxn]; type lx[maxn], ly[maxn]; type w[maxn][maxn]; type slack[maxn]; bool dfs(int x) { visx[x] = 1; for (int y = 0; y < m; ++y) { if (visy[y]) continue; type tmp = lx[x] + ly[y] - w[x][y]; if
(tmp == 0) { visy[y] = 1; if (matchy[y] == -1 || dfs(matchy[y])) { matchx[x] = y; matchy[y] = x; return true; } } else { slack[y] = min(slack[y], tmp); } } return false; } void KM() { memset(matchy, -1, sizeof(matchy)); memset(ly, 0, sizeof(ly)); for (int i = 0; i < n; ++i) { lx[i] = -inf; for (int j = 0; j < m; ++j) { lx[i] = max(lx[i], w[i][j]); } } for (int i = 0; i < n; ++i) { for (int j = 0; j < m; ++j) slack[j] = inf; while (1) { memset(visx, 0, sizeof(visx)); memset(visy, 0, sizeof(visy)); if (dfs(i)) break; type d = inf; for (int j = 0; j < m; ++j) { if (!visy[j]) d = min(d, slack[j]); } for (int j = 0; j < n; ++j) { if (visx[j]) lx[j] -= d; } for (int j = 0; j < m; ++j) { if (visy[j]) ly[j] += d; else slack[j] -= d; } } } } int main() { while (scanf("%d", &n) == 1) { m = n; for (int i = 0; i < n; ++i) { for (int j = 0; j < m; ++j) { scanf("%d", &w[i][j]); } } KM(); int ans = 0; for(int i=0;i<n;++i){ printf("%d%c",lx[i],i+1==n?'\n':' '); ans+=lx[i]; } for(int i=0;i<m;++i){ printf("%d%c",ly[i],i+1==m?'\n':' '); ans+=ly[i]; } printf("%d\n", ans); } return 0; }