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\n",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; }