ural 1076 Trash 二分图KM

B - Trash
Time Limit:1000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u

Description

You were just hired as CEO of the local junkyard.One of your jobs is dealing with the incoming trash and sorting it for recycling.The trash comes every day in  N containers and each of these containers contains certain amount of each of the  N types of trash. Given the amount of trash in the containers find the optimal way to sort the trash. Sorting the trash means putting every type of trash in separate container. Each of the given containers has infinite capacity. The effort for moving one unit of trash from container  i to  j is 1 if  i ≠   j otherwise it is 0.You are to minimize the total effort.

Input

The first line contains the number  N (1 ≤   N ≤ 150), the rest of the input contains the descriptions of the containers.The (1 +   i)-th line contains the description of the  i-th container the  j-th amount (0 ≤ amount ≤ 100) on this line denotes the amount of the  j-th type of trash in the  i-th container.

Output

You should write the minimal effort that is required for sorting the trash.

Sample Input

input output
4
62 41 86 94
73 58 11 12
69 93 89 88
81 40 69 13
650
/*
KM 算法复杂度O(V^3)
左边N 个点0..N-1 右边M 个点0..M-1 求匹配要保证N <=
M(swap(N,M))否则循环无法终止
mat[i][j]表示i 和j 之间有边,
lx[],ly[]是X,Y 上各点的标号
linky[y] = x 代表y 被x 匹配未匹配点linky[] = -1
最小权匹配可将权值取相反数或用一个大数去减
*/
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int MAXN = 502;
const int MAXV = 0x3F3F3F3F;
int mat[MAXN][MAXN];
bool visitx[MAXN],visity[MAXN];
int lx[MAXN],ly[MAXN],linky[MAXN];
int N,M,slack[MAXN];
int find(int u) {
	visitx[u] = 1;
	for (int i = 0;i < M;i++) {
		if (!visity[i]) {
			int tmp = lx[u] + ly[i] - mat[u][i];
			if (tmp == 0) {
				visity[i] = 1;
				if (linky[i] == -1 || find(linky[i])) {
					linky[i] = u;
					return 1;
				}
			}
			else if (tmp < slack[i])
				slack[i] = tmp;
		}
	}
	return 0;
}
int KM() {
	for (int i = 0;i < N;i++) {
		lx[i] = 0;
		for (int j = 0;j < M;j++)
			if (mat[i][j] > lx[i])
				lx[i] = mat[i][j];
	}
	for (int j = 0;j < M;j++)
		ly[j] = 0;
	memset(linky,-1,sizeof(linky));
	for (int i = 0;i < N;i++) {
		for (int j = 0;j < M;j++)
			slack[j] = MAXV;
		while (1) {
			memset(visitx,0,sizeof(visitx));
			memset(visity,0,sizeof(visity));
			int min = MAXV;
			if (find(i)) break;
			for (int j = 0;j < M;j++)
				if (!visity[j] && slack[j] < min)
					min = slack[j];
			for (int j = 0;j < N;j++)
				if (visitx[j])
					lx[j] -= min;
			for (int j = 0;j < M;j++)
				if (visity[j])
					ly[j] += min;
				else
					slack[j] -= min;
		}
	}
	int ret = 0,cnt = 0;
	for (int i = 0;i < M;i++) {
		int v = linky[i];
		if (v >= 0&& mat[v][i] != -MAXV) {
			cnt++;
			ret += mat[v][i];
		}
	}
	if (cnt < N) //不存在最佳匹配
		return -1;
	else
		return ret;
}
int main() {
	while (~scanf("%d",&N)) {
		M = N;
		int sum = 0;
		for (int i = 0;i < N;i++)
			for (int j = 0;j < M;j++)
			{
				scanf("%d",&mat[i][j]);
				sum += mat[i][j];
			}
		int ans = sum - KM();
		printf("%d\n",ans);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值