zoukankan      html  css  js  c++  java
  • 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;
    }
  • 相关阅读:
    ecstore前台模板变量处理
    MySQL慢查询日志总结
    Linux 操作MySQL常用命令行
    CGI、FastCGI和php-fpm的概念和区别
    sync实现windows与nginx主机端文件同步(参考文档)
    PHP实现四种基本排序算法
    http和https协议
    linux的计划任务
    php封装一个接口类
    mysql的常见面试问题
  • 原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/8087160.html
Copyright © 2011-2022 走看看