H - Matrix sum
Problem Description
Input
首行一個數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)
Output
Sample Input
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
Sample Output
6
費用流,取的不能少於某個值,那麼他的反面就是某行某列最多取那麼多個數,變成了普通下界爲0,上界爲特定值的普通費用流問題了。 總數 - 最大費用最大流即可
還有,剛開始我一直調試不出哪裏錯,後來發現這道題目求的是最小費用流,而不是最小費用最大流,所以要把循環退出條件改爲的、D[t] >= 0;
摘自百度某人的回答:
最小費用最大流是指:滿足最大流的情況下,讓費用最小。 最小費用流:僅要求費用最小,通常情況下有費用爲負的邊權(如果費用全爲正,那麼可以讓流量爲0,費用也就是0),可以使用最小費用最大流的算法求解,只不過終止條件變爲“從原點到匯點的費用爲正” 最小費用最大流算法的原本終止條件爲“從原點到匯點的容量爲0”
代碼:
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <math.h>
#include <queue>
#include <set>
#include <algorithm>
#include <stdlib.h>
using namespace std;
#define ll int
#define N 220
#define M 12345
#define inf (1<<29)
//注意 點標必須是 [0 - 匯點]
//雙向邊,注意RE
struct Edge{
ll from, to, flow, cap, nex, cost;
}edge[M*2];
ll head[N], edgenum;
void add(ll u,ll v,ll cap,ll cost){//網絡流要加反向弧
Edge E={u, v, 0, cap, head[u], cost};
edge[edgenum]=E;
head[u]=edgenum++;
Edge E2={v, u, 0, 0, head[v], -cost}; //這裏的cap若是單向邊要爲0
edge[edgenum]=E2;
head[v]=edgenum++;
}
ll D[N], P[N], A[N];
bool inq[N];
bool BellmanFord(ll s, ll t, ll &flow, ll &cost){
for(ll i=0;i<=t;i++) D[i]= inf;
memset(inq, 0, sizeof(inq));
D[s]=0; inq[s]=1; P[s]=0; A[s]=inf;
queue<ll> Q;
Q.push( s );
while( !Q.empty()){
ll u = Q.front(); Q.pop();
inq[u]=0;
for(ll i=head[u]; i!=-1; i=edge[i].nex){
Edge &E = edge[i];
if(E.cap > E.flow && D[E.to] > D[u] + E.cost){
D[E.to] = D[u] + E.cost ;
P[E.to] = 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;
flow += A[t];
cost += D[t] * A[t];
ll u = t;
while(u != s){
edge[P[u]].flow += A[t];
edge[P[u]^1].flow -= A[t];
u = edge[P[u]].from;
}
return true;
}
ll flow;
ll Mincost(ll s,ll t){//返回最小費用
flow = 0 ; ll cost = 0;
while(BellmanFord(s, t, flow, cost));
return cost;
}
void init(){memset(head,-1,sizeof head); edgenum = 0;}
int n, m, k ;
int mp[55][55];
int R[55],C[55];
int main(){
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
int T;
scanf("%d",&T);
while(T--){
cin>>n>>m;
int sum = 0;
for(int i = 1; i <= n; i++)
{
for(int j = 1; j<=m; j++)
{
scanf("%d",&mp[i][j]);
sum += mp[i][j];
}
}
for(int i = 1;i <= n; i++) scanf("%d",&R[i]);
for(int i = 1;i <= m; i++) scanf("%d",&C[i]);
init();
int from = 0, to = n+m+1;
for(int i = 1;i <= n; i++) add(from, i, m - R[i], 0);
for(int i = 1;i <= m; i++) add(i + n, to, n - C[i], 0);
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= m; j++)
{
add(i, j+n , 1 , -mp[i][j]);
}
}
int ans = -Mincost(from, to);
// cout<<flow<<endl;
ans = sum - ans;
cout<<ans<<endl;
}
return 0;
}