[费用流] acdream 1171 Matrix sum

LINK: http://acdream.info/problem?pid=1171
    太弱没有想到用费用流做,每行每列至少选x个,相对立的是 每行每列至少选0个,至多N-x个的情况下选取的总和最大。
    这就成了最大费用最大流。(要费用最大,流量一定最大了) 
    源点S和1....n (对应n行)建边流量为M-x,费用为0,n+1,n+2,....n+m(对应m列)和T建边,流量为N-x,费用为0

    1....n和 n+1,n+2.....n+m分别建边流量为1,费用为num[i][j];

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<cmath>
#include<queue>
#include<map>
#include<set>
using namespace std;
#define INF 1000000000
//typedef __int64 LL;
#define N 150
int n, m, num[N][N], S, T;
int tot, hh[N], nn[N], mm[N], sum;
int inq[N], dis[N], pre[N];
struct node
{
    int u, v, flow, cost, next;
}edge[1000000];
void add(int u, int v, int flow,int cost)
{
    edge[tot].u=u; edge[tot].v=v;
    edge[tot].flow=flow; edge[tot].cost = cost;
    edge[tot].next=hh[u]; hh[u] =tot++;
}
void add_(int u, int v, int flow, int cost)
{
    add(u, v, flow, cost); add(v, u, 0, -cost);
}
void init()
{
    memset(hh, -1, sizeof(hh));
    tot = 0;
}
void creat()
{
    init();
    S=0; T= n+m+1;
    for(int i=1; i<=n; i++)
    {
        add_(S, i, m-nn[i], 0);
    }
    for(int i=1; i<=m; i++)
    {
        add_(i+n, T, n-mm[i], 0);
    }
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=m; j++)
        {
            add_(i, n+j, 1, num[i][j]);
        }
    }
}
int bfs()
{
    queue<int> Q;
    memset(inq, 0, sizeof(inq));
    for(int i=0; i<=T; i++) dis[i] = -INF, pre[i] = -1;
    dis[S] = 0;
    Q.push(S); inq[S] = 1;
    while(!Q.empty())
    {
        int u = Q.front(); Q.pop();
        inq[u] = 0;
        for(int i=hh[u]; i!=-1; i=edge[i].next)
        {
            int v = edge[i].v;
            int flow = edge[i].flow, cost = edge[i].cost;
            if(flow && dis[v] < dis[u] +cost)
            {
                dis[v] = dis[u] +cost;
                pre[v] = i;
                if(!inq[v])
                {
                    inq[v] = 1; Q.push(v);
                }
            }
        }
    }
    return dis[T]>=0;
}
int f()
{
    int flow, cost;
    flow = cost = 0 ;
    while(bfs())
    {
        int tmp = INF;
        for(int i=pre[T]; i!=-1; i=pre[edge[i].u])
        {
            tmp = min(tmp, edge[i].flow);
        }
        flow += tmp;
        cost += dis[T]*tmp;
        for(int i=pre[T]; i!=-1; i=pre[edge[i].u])
            edge[i].flow -= tmp, edge[i^1].flow += tmp;
    }
    return sum - cost;
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
#endif // ONLINE_JUDGE
    int t;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d%d",&n, &m);
        sum = 0;
        for(int i=1;i<=n; i++)
        {
            for(int j=1; j<=m; j++)
            {
                scanf("%d", &num[i][j]); sum += num[i][j];
            }
        }
        for(int i=1; i<=n; i++) scanf("%d", &nn[i]);
        for(int i=1; i<=m; i++) scanf("%d", &mm[i]);
        creat();
        printf("%d\n", f());
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值