给你一个m x n (1 <= m, n <= 100)的矩阵A (0<=aij<=10000),要求在矩阵中选择一些数,要求每一行,每一列都至少选到了一个数,使得选出的数的和尽量的小。
这个题目有个限制,不然就非常简单了,每行每列至少选择了一个数,即每行可以多选,这与平常所做的那种简单的最小费用最大流有些不一样。
那么在建图上我们需要做出一些变化
还是那样每行每列都对应一个结点
我们需要强制其每行每列都选择到了数
首先源点向每个行结点连容量为1,花费为-100000的边
每个列结点向汇点连容量为1,花费为-100000的边,
然后行结点跟列结点之间,就连容量为1,花费为相应矩阵中的数值
接着
源点向每个行结点连容量为INF,花费为0的边,
每个列结点向汇点连容量为INF,花费为0的边
这样建图之后,肯定会优先走那些花费为负的边,保证了每一行每一列都选到了点
可以自己画图试试,就可以知道为什么这样可以
然后最后的结果由于多加了一些负数,所以要变化一下
/* ID: CUGB-wwj PROG: LANG: C++ */ #include <iostream> #include <vector> #include <list> #include <map> #include <set> #include <deque> #include <queue> #include <stack> #include <bitset> #include <algorithm> #include <functional> #include <numeric> #include <utility> #include <sstream> #include <iomanip> #include <cstdio> #include <cmath> #include <cstdlib> #include <cctype> #include <string> #include <cstring> #include <cmath> #include <ctime> #define INF 111111 #define MAXN 222 #define MAXM 55555 #define L(x) x<<1 #define R(x) x<<1|1 #define eps 1e-4 using namespace std; struct EDGE { int v, cap, cost, next, re; // re记录逆边的下标。 } edge[MAXM]; int n, m, ans, flow, src, des; int e, head[MAXN]; int que[MAXN], pre[MAXN], dis[MAXN]; bool vis[MAXN]; void init() { e = ans = flow = 0; memset(head, -1, sizeof(head)); } void add(int u, int v, int cap, int cost) { edge[e].v = v; edge[e].cap = cap; edge[e].cost = cost; edge[e].next = head[u]; edge[e].re = e + 1; head[u] = e++; edge[e].v = u; edge[e].cap = 0; edge[e].cost = -cost; edge[e].next = head[v]; edge[e].re = e - 1; head[v] = e++; } bool spfa() { int i, h = 0, t = 1; for(i = 0; i <= n; i ++) { dis[i] = INF; vis[i] = false; } dis[src] = 0; que[0] = src; vis[src] = true; while(t != h) { int u = que[h++]; h %= n; vis[u] = false; for(i = head[u]; i != -1; i = edge[i].next) { int v = edge[i].v; if(edge[i].cap && dis[v] > dis[u] + edge[i].cost) { dis[v] = dis[u] + edge[i].cost; pre[v] = i; if(!vis[v]) { vis[v] = true; que[t++] = v; t %= n; } } } } if(dis[des] == INF) return false; return true; } void end() { int u, p, mi = INF; for(u = des; u != src; u = edge[edge[p].re].v) { p = pre[u]; mi = min(mi, edge[p].cap); } for(u = des; u != src; u = edge[edge[p].re].v) { p = pre[u]; edge[p].cap -= mi; edge[edge[p].re].cap += mi; ans += mi * edge[p].cost; // cost记录的为单位流量费用,必须得乘以流量。 } flow += mi; } int nt; void build() { scanf("%d%d", &nt, &m); src = nt + m + 1; des = nt + m + 2; n = des; int num; for(int i = 1; i <= nt; i++) for(int j = 1; j <= m; j++) { scanf("%d", &num); add(i, j + nt, 1, num); } for(int i = 1; i <= nt; i++) { add(src, i, 1, -100000); add(src, i, INF, 0); } for(int i = 1; i <= m; i++) { add(i + nt, des, 1, -100000); add(i + nt, des, INF, 0); } } void MCMF() { init(); build(); while(spfa() && dis[des] < 0) end(); } int main() { int T; int cas = 0; scanf("%d", &T); while(T--) { MCMF(); printf("Case %d: %d\n", ++cas, (nt + m) * 100000 + ans); } return 0; }