[题解] uva 11383 Golden Tiger Claw (二分图最佳完美匹配)

- 传送门 -

 https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2378

# 11383 - Golden Tiger Claw Time limit: 8.000 seconds

| Root | Submit Problem Stats
uDebug Download as PDF |

(题面见pdf)

 

- 题意 -

 对于一个 N*N 矩阵, 每个格子有一个整数w(i,j), 对每行确定一个数row(i), 每一列确定一个数col(i), 使对于任意格子(i,j), w(i,j)<=row(i)+col(i), 所有的 row 和 col 的和要最小.
 

- 思路 -

 发现(通过题解得知)这个 col 和 row 很像最佳匹配中的两个标杆...
 我们把列数和行数视为两个集合中的点, 边权就是格点上的数.
 对于任意一个点, 它和对面集合中的点连边的边权都小于两边标杆之和, 满足题意...
 km算法之后所有顶标之和是最小的(大概...是因为...就是...那个...记住就行...), 据此得出答案.
 反正就是个脑洞题呗...

 细节见代码.
 

- 代码 -

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int inf = 0x7f7f7f7f;
const int N = 500 + 5;

int W[N][N], LX[N], LY[N];
int SLACK[N], S[N], T[N], ANS[N];
int n;

bool find(int x) {
	S[x] = true;
	
	for (int i = 1; i <= n; ++i) {
		if (T[i]) continue;
		
		int tmp = LX[x] + LY[i] - W[x][i];
		if (tmp == 0) {
			T[i] = true;
			if (ANS[i] == 0 || find(ANS[i])) {
				ANS[i] = x;
				return true;
			}
		}
		else if (tmp < SLACK[i])
			SLACK[i] = tmp;
	}
	
	return false;
}

int km() {
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= n; ++j)
			SLACK[j] = inf;
		while (true) {
			memset(S, 0, sizeof (S));
			memset(T, 0, sizeof (T));
			
			if (find(i)) break;
			
			int d = inf;
			for (int j = 1; j <= n; ++j) {
				if (!T[j] && d > SLACK[j])
					d = SLACK[j];
			}
			
			for (int j = 1; j <= n; ++j) {
				if (S[j]) LX[j] -= d;
			}
			
			for (int j = 1; j <= n; ++j) {
				if (T[j]) LY[j] += d;
				else SLACK[j] -= d;
			}
			
		}
	}
	int sum = 0;
	for (int i = 1; i <= n; ++i)	
		sum += LX[i] + LY[i];
	return sum;
}

int main() {
	
	while(~scanf("%d", &n)) {
		memset(LX, 0, sizeof (LX));
		memset(LY, 0, sizeof (LY));
		memset(ANS, 0, sizeof (ANS));
		for (int i = 1; i <= n; ++i) {
			for (int j = 1; j <= n; ++j) {
				scanf("%d", &W[i][j]);
				LX[i] = max(LX[i], W[i][j]);
			}
		}

		int ans = km();

		printf("%d", LX[1]);
		for (int i = 2; i <= n; ++i)
			printf(" %d", LX[i]);
		printf("\n");
		printf("%d", LY[1]);
		for (int i = 2; i <= n; ++i)
			printf(" %d", LY[i]);
		printf("\n");
		printf("%d\n", ans);
	}
	return 0;
	
}
posted @ 2017-09-03 17:20  lstttt  阅读(133)  评论(0编辑  收藏  举报