大赛得分及排名规则详见:http://
Official Analysis: http://weibo.com/2685713683/B0aWBfQwk
Problem:http://hihocoder.com/contest/msbopqual/problem/3
P3 : 格格取數
-
2 3 3 1 2 3 3 1 2 2 3 1 5 5 1 2 3 4 5 5 1 2 3 4 4 5 1 2 3 3 4 5 1 2 2 3 4 5 1
Sample Output
-
Case 1: 3 Case 2: 5
題目描述
給你一個m x n (1 <= m, n <= 100)的矩陣A(0<=aij<=10000),要求在矩陣中選擇一些數,要求每一行,每一列都至少選到了一個數,使得選出的數的和儘量的小。
輸入
多組測試資料。首先是資料組數T
對於每組測試資料,第1列是兩個正整數m, n,分別表示矩陣的列數和行數。
接下來的m列,每列n個整數,之間用一個空格分隔,表示矩陣A的元素。
輸出
每組資料輸出一列,表示選出的數的和的最小值。
資料範圍
小資料:1 <= m, n <= 5
大資料:1 <= m, n <= 100
Analysis:
Construct bipartite graph: each row/column becomes a vertex; number in matrix becomes edge weight
Problem: min-cost bipartite edge-cover (http://cstheory.stackexchange.com/questions/14690/reducing-a-minimum-cost-edge-cover-problem-to-minimum-cost-weighted-bipartie-per)
If all edge weights are non-negative, then the minimum weight set of edges that covers all the nodes automatically has the property that it has no three-edge paths, because the middle edge of any such path would be redundant. If we assign each vertex to an edge that covers it, some edges will cover both of their endpoints (forming a matching M ) and others will cover only one of their endpoints (and must be the minimum weight edge adjacent to the covered endpoint). If we let cv be the cost of the minimum weight edge incident to vertex v , and we be the weight of e , then the cost of a solution is ∑v∈Gcv+∑(u,v)∈M(w(u,v)−cu−cv) . The first sum doesn't depend on the choice of the cover, so the problem becomes one of finding a matching that maximizes the total weight, for edge weights cu+cv−w(u,v) . If you really want this to be a minimum weight perfect matching problem, then instead use weights w(u,v)−cu−cv and add enough dummy edges with weight zero to guarantee that any matching with the real edges can be extended to a perfect matching by adding dummy edges.
If the input graph can have negative edge weights, then the three-edge-path constraint becomes meaningful. In this case it's not obvious to me that there is a polynomial time solution.
Min-cost bipartite edge-cover ==> maximum weighted bipartite matching
References:
http://en.wikipedia.org/wiki/Edge_cover
http://en.wikipedia.org/wiki/Matching_(graph_theory)#In_weighted_bipartite_graphs
http://www.nocow.cn/index.php/Kuhn-Munkres%E7%AE%97%E6%B3%95
http://www.cnblogs.com/mcflurry/archive/2013/01/24/2874114.html
http://en.wikipedia.org/wiki/Hungarian_algorithm
Algorithm:
1. construct graph; transform graph by changing weights to c_u + c_v - w_{u,v} (in addition, add zero-weight edges to substitute negative-weight edges)
2. compute max weight (perfect) matching M by using KM-Algorithm
3. For vertices not covered by edges in M, select covering edge with weight c_v
C++ Code:
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
const int N = 128;
const int INF = 1 << 28;
class Graph {
private:
bool xckd[N], yckd[N];
int n, edge[N][N], xmate[N], ymate[N];
int lx[N], ly[N], slack[N], prev[N];
queue<int> Q;
bool bfs();
void agument(int);
public:
void make(int, int, int[N][N]);
int KMMatch();
};
void Graph::make(int nx, int ny, int mat[N][N]) {
n = max(nx, ny);
memset(edge, 0, sizeof(edge));
for (int i = 0; i < nx; ++i)
for (int j = 0; j < ny; ++j)
edge[i][j] = mat[i][j];
}
bool Graph::bfs() {
while(!Q.empty()) {
int p = Q.front(), u = p>>1; Q.pop();
if(p&1) {
if(ymate[u] == -1) { agument(u); return true; }
else { xckd[ymate[u]] = true; Q.push(ymate[u]<<1); }
} else {
for(int i = 0; i < n; i++)
if(yckd[i]) continue;
else if(lx[u]+ly[i] != edge[u][i]) {
int ex = lx[u]+ly[i]-edge[u][i];
if(slack[i] > ex) { slack[i] = ex; prev[i] = u; }
} else {
yckd[i] = true; prev[i] = u;
Q.push((i<<1)|1);
}
}
}
return false;
}
void Graph::agument(int u) {
while(u != -1) {
int pv = xmate[prev[u]];
ymate[u] = prev[u]; xmate[prev[u]] = u;
u = pv;
}
}
int Graph::KMMatch() {
memset(ly, 0, sizeof(ly));
for(int i = 0; i < n; i++) {
lx[i] = -INF;
for(int j = 0; j < n; j++) lx[i] = max(lx[i], edge[i][j]);
}
memset(xmate, -1, sizeof(xmate)); memset(ymate, -1, sizeof(ymate));
bool agu = true;
for(int mn = 0; mn < n; mn++) {
if(agu) {
memset(xckd, false, sizeof(xckd));
memset(yckd, false, sizeof(yckd));
for(int i = 0; i < n; i++) slack[i] = INF;
while(!Q.empty()) Q.pop();
xckd[mn] = true; Q.push(mn<<1);
}
if(bfs()) { agu = true; continue; }
int ex = INF; mn--; agu = false;
for(int i = 0; i < n; i++)
if(!yckd[i]) ex = min(ex, slack[i]);
for(int i = 0; i < n; i++) {
if(xckd[i]) lx[i] -= ex;
if(yckd[i]) ly[i] += ex;
slack[i] -= ex;
}
for(int i = 0; i < n; i++)
if(!yckd[i] && slack[i] == 0) { yckd[i] = true; Q.push((i<<1)|1); }
}
int cost = 0;
for(int i = 0; i < n; i++) cost += edge[i][xmate[i]];
return cost;
}
int main() {
int T;
scanf("%d", &T);
for (int casenum = 1; casenum <= T; ++casenum) {
int m, n, mat[N][N];
scanf("%d%d", &m, &n);
for (int i = 0; i < m; ++i)
for (int j = 0; j < n; ++j)
scanf("%d", &mat[i][j]);
int cr[N], cc[N], ans = 0;
for (int i = 0; i < m; ++i) {
cr[i] = mat[i][0];
for (int j = 1; j < n; ++j)
cr[i] = min(cr[i], mat[i][j]);
ans += cr[i];
}
for (int j = 0; j < n; ++j) {
cc[j] = mat[0][j];
for (int i = 0; i < m; ++i)
cc[j] = min(cc[j], mat[i][j]);
ans += cc[j];
}
for (int i = 0; i < m; ++i)
for (int j = 0; j < n; ++j)
mat[i][j] = max(cr[i] + cc[j] - mat[i][j], 0);
Graph g;
g.make(m, n, mat);
printf("Case %d: %d\n", casenum, ans - g.KMMatch());
}
return 0;
}