首行一个数T(T <= 40),代表数据总数,接下来有T组数据。
每组数据:
第一行两个数N,M(1 <= N,M <= 50)
接下来N行,每行M个数(范围是0-10000的整数)
接下来一行有N个数Ki,表示第i行至少选Ki个元素(0 <= Ki <= M)
最后一行有M个数Kj,表示第j列至少选Kj个元素(0 <= Kj <= N)
1 4 4 1 1 1 1 1 10 10 10 1 10 10 10 1 10 10 10 1 1 1 1 1 1 1 1
6
分析:
很容易想到二分图模型(n行左端点,m列右端点) --> 有上下界的费用流
每行每列取数的个数不能少于R[i] / C[i], 问取得数总和最小是多少Min_Sum?
转化为
每行每列取数的个数不多于 m-R[i] / n - C[i],问取得数总和最大是多少Max_Sum?
Min_Sum = All_Sum - Max_Sum
总数 - 最大费用最大流即可
这样就把有上下界的费用流问题转化为(只有上界)普通的费用流问题了。
#include<cstdio> #include<cstring> #include<queue> #include<vector> #include<algorithm> using namespace std; const int maxn = 202 + 10; const int INF = 1000000000; typedef long long LL; struct Edge { int from, to, cap, flow, cost; }; struct MCMF { //<span style="font-size:14px;">最大费用最大流</span> int n, m, s, t; vector<Edge> edges; vector<int> G[maxn]; int inq[maxn]; // 是否在队列中 int d[maxn]; // Bellman-Ford int p[maxn]; // 上一条弧 int a[maxn]; // 可改进量 void init(int n) { this->n = n; for(int i = 0; i < n; i++) G[i].clear(); edges.clear(); } void AddEdge(int from, int to, int cap, int cost) { edges.push_back((Edge){from, to, cap, 0, cost}); edges.push_back((Edge){to, from, 0, 0, -cost}); m = edges.size(); G[from].push_back(m-2); G[to].push_back(m-1); } bool BellmanFord(int s, int t, LL& ans) { for(int i = 0; i <= t; i++) d[i] = -INF; //与最小费用最大流相反(d[i]=INF) memset(inq, 0, sizeof(inq)); d[s] = 0; inq[s] = 1; p[s] = 0; a[s] = INF; queue<int> Q; Q.push(s); while(!Q.empty()) { int u = Q.front(); Q.pop(); inq[u] = 0; for(int i = 0; i < G[u].size(); i++) { Edge& e = edges[G[u][i]]; if(e.cap > e.flow && d[e.to] < d[u] + e.cost) { <span style="font-family: Arial, Helvetica, sans-serif;">//与最小费用最大流相反(d[e.to] < d[u] + e.cost )</span> d[e.to] = d[u] + e.cost; p[e.to] = G[u][i]; a[e.to] = min(a[u], e.cap - e.flow); if(!inq[e.to]) { Q.push(e.to); inq[e.to] = 1; } } } } if(d[t] < 0) return false; //<span style="font-family: Arial, Helvetica, sans-serif;">与最小费用最大流相反(d[i]>=0)</span> ans += (LL)d[t] * (LL)a[t]; int u = t; while(u != s) { edges[p[u]].flow += a[t]; edges[p[u]^1].flow -= a[t]; u = edges[p[u]].from; } return true; } // 需要保证初始网络中没有负权圈 LL Mincost(int s, int t) { LL cost = 0; while(BellmanFord(s, t, cost)); return cost; } }; MCMF g; int S, T; LL SUM; int a[60][60], R[60], C[60]; void init() { int n, m, i, j; scanf("%d%d", &n, &m); SUM = 0; for(i=1; i<=n; ++i) for(j=1; j<=m; ++j) { scanf("%d", &a[i][j]); SUM += a[i][j]; } for(i=1; i<=n; ++i) scanf("%d", &R[i]); for(i=1; i<=m; ++i) scanf("%d", &C[i]); S = 0, T = n + m + 1; g.init(T); for(i=1; i<=n; ++i) { g.AddEdge(S, i, m-R[i], 0); } for(i=1; i<=m; ++i) { g.AddEdge(i+n, T, n-C[i], 0); } for(i=1; i<=n; ++i) for(j=1; j<=m; ++j) { g.AddEdge(i, j+n, 1, a[i][j]); } } void solve() { LL t = g.Mincost(S, T); printf("%I64d\n", SUM - t); } int main() { int T; scanf("%d", &T); while(T--) { init(); solve(); } return 0; }
官方题解 最大费用最大流板子
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> using namespace std; const int E = 50010; const int oo = 0x7fffffff; const int N = 210; struct edge { int next,v,flow,cost; }e[E]; int head[N],cnt; queue<int> q; void addedge(int u,int v,int flow,int cost) { e[cnt].v = v; e[cnt].flow = flow; e[cnt].cost = cost; e[cnt].next = head[u]; head[u] = cnt ++; } void addEdge(int u,int v,int flow,int cost) { addedge(u,v,flow,cost); addedge(v,u,0, -cost); } int S,T; int ans; int a[N][N]; void init() { int n,m; scanf("%d%d",&n,&m); ans = 0; for(int i = 1; i <= n; i ++) for(int j = 1; j <= m; j ++) { scanf("%d",&a[i][j]); ans += a[i][j]; } int R[N],C[N]; for(int i = 1; i <= n; i ++) scanf("%d",&R[i]); for(int i = 1; i <= m; i ++) scanf("%d",&C[i]); S = 0,T = n + m + 1; cnt = 0; memset(head,-1,sizeof(head)); for(int i = 1; i <= n; i ++) { addEdge(S,i,m - R[i],0); } for(int i = 1; i <= m; i ++) addEdge(i + n,T,n - C[i],0); for(int i = 1; i <= n; i ++) for(int j = 1; j <= m; j ++) addEdge(i,j + n,1,a[i][j]); } int dis[N],cc[N],visit[N],pre[N],dd[N]; int spfa() { fill(dis,dis + T + 1, -oo); dis[S] = 0; pre[S] = -1; while(!q.empty()) q.pop(); q.push(S); while(!q.empty()) { int u = q.front(); q.pop(); visit[u] = 0; for(int i = head[u]; i != -1; i = e[i].next) { if(e[i].flow > 0 && dis[e[i].v] < dis[u] + e[i].cost) { dis[e[i].v] = dis[u] + e[i].cost; pre[e[i].v] = u; cc[e[i].v] = i; dd[e[i].v] = e[i].cost; if(!visit[e[i].v]) { q.push(e[i].v); visit[e[i].v] = 1; } } } } return dis[T] >= 0; } int argument() { int aug = oo; int u,v; int ans = 0; for(u = pre[v = T]; v != S; v = u, u = pre[v]) if(e[cc[v]].flow < aug) aug = e[cc[v]].flow; for(u = pre[v = T]; v != S; v = u, u = pre[v]) { e[cc[v]].flow -= aug; e[cc[v] ^ 1].flow += aug; ans += dd[v] * aug; } return ans; } void mcmf() { memset(visit,0,sizeof(visit)); while(spfa()) { int cost = argument(); if(ans < 0) break; ans -= cost; } printf("%d\n",ans); } int main() { //freopen("choose.in","r",stdin); //freopen("choose.out","w",stdout); int t; scanf("%d",&t); for(int cas = 1; cas <= t; cas ++) { init(); mcmf(); } return 0; }