bzoj 2406 矩阵 —— 有源汇上下界可行流
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2406
这题,首先把题目那个式子的绝对值拆成两个限制,就成了网络流的上下界;
有上下界可行流原来只需要先流出下界,然后用超级源汇补足即可,原来的汇点向源点连一条下界0上界 inf 的边,就也流量守恒了;
竟然是枚举出错了囧,因为平时写的 S=0,所以枚举就是 S~T,但这回写的 S=n+1,枚举应该变成 1~T 啊!
代码如下:
#include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; int const xn=505,xm=1e5+5,inf=1e9; int n,m,si[xn],sj[xn],hd[xn],ct=1,to[xm],nxt[xm],c[xm],dis[xn]; int tmp[xn],a[xn][xn],cur[xn],L,R,S,T,SS,TT; queue<int>q; int rd() { int ret=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=0; ch=getchar();} while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar(); return f?ret:-ret; } void ade(int x,int y,int z){to[++ct]=y; nxt[ct]=hd[x]; hd[x]=ct; c[ct]=z;} void add(int x,int y,int z){ade(x,y,z); ade(y,x,0);} bool bfs(int s,int t) { memset(dis,0,sizeof dis); dis[s]=1; q.push(s); while(q.size()) { int x=q.front(); q.pop(); for(int i=hd[x],u;i;i=nxt[i]) if(!dis[u=to[i]]&&c[i])dis[u]=dis[x]+1,q.push(u); } return dis[t]; } int dfs(int x,int fl,int t) { if(x==t)return fl; int ret=0; for(int &i=cur[x],u;i;i=nxt[i]) { if(dis[u=to[i]]!=dis[x]+1||!c[i])continue; int tmp=dfs(u,min(fl-ret,c[i]),t); if(!tmp)dis[u]=0; c[i]-=tmp; c[i^1]+=tmp; ret+=tmp; if(ret==fl)break; } return ret; } int dinic(int s,int t) { int ret=0; while(bfs(s,t)) { memcpy(cur,hd,sizeof hd); ret+=dfs(s,inf,t); } return ret; } bool ck(int mid) { ct=1; memset(hd,0,sizeof hd); memset(tmp,0,sizeof tmp); for(int i=1;i<=n;i++) { int l=max(0,si[i]-mid),r=si[i]+mid; add(S,i,r-l); tmp[S]-=l; tmp[i]+=l; } for(int j=1;j<=m;j++) { int x=n+j,l=max(0,sj[j]-mid),r=sj[j]+mid; add(x,T,r-l); tmp[x]-=l; tmp[T]+=l; } for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { add(i,n+j,R-L); tmp[i]-=L; tmp[n+j]+=L; } add(T,S,inf); int goal=0; for(int i=1;i<=T;i++)//!S if(tmp[i]>0)add(SS,i,tmp[i]),goal+=tmp[i]; else if(tmp[i]<0)add(i,TT,-tmp[i]); return goal==dinic(SS,TT); } int main() { n=rd(); m=rd(); int l=0,r=0; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) a[i][j]=rd(),si[i]+=a[i][j],sj[j]+=a[i][j],r+=a[i][j]; L=rd(); R=rd(); int ans; S=n+m+1; T=n+m+2; SS=n+m+3; TT=n+m+4; while(l<=r) { int mid=((l+r)>>1); if(ck(mid))ans=mid,r=mid-1; else l=mid+1; } printf("%d\n",ans); return 0; }