2014编程之美挑战赛资格赛

大赛得分及排名规则详见:http://programming2014.cstnet.cn/rules.html

Official Analysis: http://weibo.com/2685713683/B0aWBfQwk


Problem:http://hihocoder.com/contest/msbopqual/problem/3


P3 : 格格取數

Time Limit: 2000ms
Case Time Limit: 1000ms
Memory Limit: 256MB

題目描述

給你一個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



Sample Input
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



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  vGcv+(u,v)M(w(u,v)cucv) . 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+cvw(u,v) . If you really want this to be a minimum weight perfect matching problem, then instead use weights  w(u,v)cucv  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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值