acdream 1171 Matrix sum

H - Matrix sum

Time Limit:  8000/4000MS (Java/Others)  Memory Limit:  128000/64000KB (Java/Others)
Problem Description
sweet和zero在玩矩阵游戏,sweet画了一个N * M的矩阵,矩阵的每个格子有一个整数。zero给出N个数Ki,和M个数Kj,zero要求sweet选出一些数,满足从第 i 行至少选出了Ki个数,第j列至少选出了Kj个数。 这些数之和就是sweet要付给zero的糖果数。sweet想知道他至少要给zero多少个糖果,您能帮他做出一个最优策略吗?
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
每组数据输出一行,sweet要付给zero的糖果数最少是多少
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;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值