首行一個數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#include #include #include #include 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 { //最大費用最大流 int n, m, s, t; vector edges; vector 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 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) { //與最小費用最大流相反(d[e.to] < d[u] + e.cost ) 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; //與最小費用最大流相反(d[i]>=0) 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#include #include #include #include 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 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; }