bzoj千题计划158:bzoj2406: 矩阵(有源汇上下界可行流)

http://www.lydsy.com/JudgeOnline/problem.php?id=2406

设矩阵C=A-B

最小化 C 一行或一列和的最大值

整体考虑一行或者一列的和

二分最大值

这样每一行一列的和就有了范围

|Σai-Σbj|<=mid

去掉绝对值 Σai-mid <= Σbi <= Σai+mid

构图:

源点向行连下界为Σai-mid,上界为 Σai+mid 的边

列向汇点连下界为Σai-mid,上界为 Σai+mid 的边

第i行向第j列连下界为L,上界为R的边

上下界可行流验证

#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

#define N 405
#define M 41000

const int inf=2e9;

int n,m,L,R;

int sumh[N],suml[N];

int s,t,S,T;

int d[N];

int SUM;

int front[N],to[M<<1],nxt[M<<1],tot,val[M<<1];

int lev[N],cur[N];

void read(int &x)
{
    x=0; char c=getchar();
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
}

void add(int u,int v,int w)
{
    to[++tot]=v; nxt[tot]=front[u]; front[u]=tot; val[tot]=w;
    to[++tot]=u; nxt[tot]=front[v]; front[v]=tot; val[tot]=0;
    //printf("%d %d %d
",u,v,w);
}

void build(int mid)
{
    tot=1;
    memset(front,0,sizeof(front));
    memset(d,0,sizeof(d));
    SUM=0;
    for(int i=1;i<=n;++i) 
    {
        d[i]+=sumh[i]-mid;
        d[s]-=sumh[i]-mid;
        add(s,i,mid<<1);
    }
    for(int i=1;i<=m;++i)
    {
        d[t]+=suml[i]-mid;
        d[n+i]-=suml[i]-mid;
        add(n+i,t,mid<<1);
    }
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j)
        {
            d[n+j]+=L;
            d[i]-=L;
            add(i,n+j,R-L);
        }
    for(int i=1;i<=t;++i) 
    {
        if(d[i]>0) add(S,i,d[i]),SUM+=d[i];
        else if(d[i]<0) add(i,T,-d[i]);
    }
    add(t,s,inf);
}

bool bfs()
{
    for(int i=S;i<=T;++i) lev[i]=-1,cur[i]=front[i];
    std::queue<int>q;
    q.push(S);
    lev[S]=0;
    int now;
    while(!q.empty())
    {
        now=q.front();
        q.pop();
        for(int i=front[now];i;i=nxt[i])
            if(lev[to[i]]==-1 && val[i])
            {
                lev[to[i]]=lev[now]+1;
                if(to[i]==T) return true;
                q.push(to[i]);
            }
    }
    return false;
}

int dinic(int now,int flow)
{
    if(now==T) return flow;
    int rest=0,delta;
    for(int &i=cur[now];i;i=nxt[i])
        if(lev[to[i]]==lev[now]+1 && val[i])
        {
            delta=dinic(to[i],std::min(flow-rest,val[i]));
            if(delta)
            {
                val[i]-=delta;
                val[i^1]+=delta;
                rest+=delta;
                if(rest==flow) break;
            }
        }
    if(rest!=flow) lev[now]=-1;
    return rest;
}

int maxflow()
{
    int now=0;
    while(bfs()) now+=dinic(S,inf);
    return now;
}

bool check(int mid)
{
    build(mid);
    return SUM==maxflow();
}

int main()
{
    read(n); read(m);
    S=0; s=n+m+1; t=n+m+2; T=n+m+3;
    int x;
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j)
        {
            read(x);
            sumh[i]+=x;
            suml[j]+=x;
        }
    read(L); read(R);
    int l=0,r=2e6,mid,ans=-1;
    while(l<=r)
    {
        mid=l+r>>1;
        if(check(mid)) ans=mid,r=mid-1;
        else l=mid+1;
    }
    std::cout<<ans;
}
原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/8087160.html