最小费用流

描述

给你一个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



样例输入
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
样例输出
Case 1: 3
Case 2: 5
题意:给出一个矩阵,在矩阵中抽数,使得每一行每一列都有被取到。取出来的数和最小
思路:最小费用流。超级源点连向行,流量为c-1,费用0。列连向超级汇点,流量为r-1,费用为0。行和列相连,流量为1,费用为-key。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define maxn 1800
#define maxm 38000
#define inf 0x3f3f3f3f
int first[maxn];
int key[108][108];
int vv[maxm],ww[maxm],nxt[maxm],cst[maxm];
int e;
int pre[maxn],pos[maxn];
int dis[maxn],que[maxn*10];
bool vis[maxn];
inline int min(int a,int b)
{
	return a > b?b:a;
}
void addEdge(int u,int v,int w,int cost)
{
	vv[e] = v;
	ww[e] = w;
	cst[e] = cost;
	nxt[e] = first[u];
	first[u] = e++;
	vv[e] = u;
	ww[e] = 0;
	cst[e] = -cost;
	nxt[e] = first[v];
	first[v] = e++;
}

int spfa(int s,int t)
{
	memset(pre,-1,sizeof(pre));
	memset(vis,0,sizeof(vis));
	int head,tail;
	head = tail = 0;
	for(int i = 0;i < maxn;i++)
		dis[i] = inf;
	que[tail++] = s;
	pre[s] = s;
	dis[s] = 0;
	vis[s] = 1;
	while(head < tail)
	{
		int u = que[head++];
		vis[u] = 0;
		for(int i = first[u];i != -1;i = nxt[i])
		{
			int v = vv[i];
			if(ww[i] > 0 && dis[u] + cst[i] < dis[v])
			{
				dis[v] = dis[u] + cst[i];
				pre[v] = u;
				pos[v] = i;
				if(!vis[v])
				{
					vis[v] = 1;
					que[tail++] = v;
				}
			}
		}
	}
	return dis[t];
}

int MinCostFlow(int s,int t)
{
	int cost = 0;
	while(1)
	{
		int add = spfa(s,t);
		if(add >= 0)	break;
		cost += add;
		for(int i = t;i != s;i = pre[i])
		{
			ww[pos[i]] -= 1;
			ww[pos[i]^1] += 1;
		}
	}
	return cost;
}

int main()
{
	//freopen("in.txt","r",stdin);
	int t,cas = 0;
	scanf("%d",&t);
	while(t--)
	{
		int r,c;
		int sum = 0;
		scanf("%d%d",&r,&c);
		for(int i = 1;i <= r;i++)
		{
			for(int j = 1;j <= c;j++)
			{
				scanf("%d",&key[i][j]);
				sum += key[i][j];
			}
		}
		e = 0;
		memset(first,-1,sizeof(first));
		for(int i = 1;i <= r;i++)
			addEdge(0,i,c-1,0);
		for(int i = r+1;i <= r+c;i++)
			addEdge(i,r+c+1,r-1,0);
		for(int i = 1;i <= r;i++)
		{
			for(int j = r+1;j <= r+c;j++)
			{
				addEdge(i,j,1,-key[i][j-r]);
			}
		}
		int ans = -MinCostFlow(0,r+c+1);
		ans = sum - ans;
		printf("Case %d: %d\n",++cas,ans);
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值