[上下界网络流][二分] Bzoj P2406 矩阵
题目描述
输入输出格式
输入格式:
第一行两个数n、m,表示矩阵的大小。
接下来n行,每行m列,描述矩阵A。
最后一行两个数L,R。
输出格式:
第一行,输出最小的答案;
输入输出样例
说明
对于100%的数据满足N,M<=200,0<=L<=R<=1000,0<=A_{ij}Aij<=1000
题解
- 这题的转换实在是太TM的神奇了!!!
- 首先,题目要求的其实是最大值最小,那么我们可以二分,将其转换为判断性问题
- 乍一看这个式子,一脸懵逼,再乍一看,其实就是要我们“最小化每行每列所有元素与给定矩阵差的和的绝对值中的最大值”
- 然后先设h[i]为第i行的和,l[i]为第i列的和
- 那么如果要满足二分出来的mid,那么一定要满足B矩阵的第i行总和为[r[i]-mid,mid+r[i]],同列的话也是一样的
- 那么我们就可以把每列每行的看成一个点,原点向每个行点连边,上下界为[r[i]-mid,mid+r[i]],列点向汇点连边,上下界为[c[i]-mid,mid+c[i]]
- 然后行列之间连边上下界为[L,R],这样的话,问题就可以转换为是否存在二分图中是否存在一个可行流
- 那么跑一遍上下界网络流就好了
代码
1 #include <cstdio> 2 #include <iostream> 3 #include <cstring> 4 #include <queue> 5 #include <algorithm> 6 using namespace std; 7 const int N=510; 8 const int inf=0x3f3f3f3f; 9 int n,m,cnt,s,t,S,T,head[N],dis[N],cur[N],d[N],h[N],l[N],L,R,num[N][N]; 10 struct edge{int to,from,v;}e[N*N*2]; 11 queue<int> Q; 12 void insert(int x,int y,int v) 13 { 14 e[++cnt].to=y,e[cnt].from=head[x],e[cnt].v=v,head[x]=cnt; 15 e[++cnt].to=x,e[cnt].from=head[y],e[cnt].v=0,head[y]=cnt; 16 } 17 int dfs(int x,int maxf) 18 { 19 if (x==T||!maxf) return maxf; 20 int ret=0; 21 for (int &i=cur[x];i;i=e[i].from) 22 if (e[i].v&&dis[e[i].to]==dis[x]+1) 23 { 24 int f=dfs(e[i].to,min(e[i].v,maxf-ret)); 25 e[i].v-=f,e[i^1].v+=f,ret+=f; 26 if (maxf==ret) break; 27 } 28 return ret; 29 } 30 bool bfs() 31 { 32 for (int i=s;i<=T;i++) dis[i]=0; 33 while (!Q.empty()) Q.pop(); 34 dis[S]=1,Q.push(S); 35 while (!Q.empty()) 36 { 37 int u=Q.front(); Q.pop(); 38 for (int i=head[u];i;i=e[i].from) 39 if (e[i].v&&!dis[e[i].to]) 40 { 41 dis[e[i].to]=dis[u]+1; 42 if (e[i].to==T) return 1; 43 Q.push(e[i].to); 44 } 45 } 46 return 0; 47 } 48 int dinic() 49 { 50 int ans=0; 51 while (bfs()) 52 { 53 for (int i=0;i<=T;i++) cur[i]=head[i]; 54 ans+=dfs(S,inf); 55 } 56 return ans; 57 } 58 bool check(int x) 59 { 60 cnt=1,memset(d,0,sizeof(d)),memset(head,0,sizeof(head)),s=0,t=n+m+1,S=n+m+2,T=n+m+3; 61 insert(t,s,inf); 62 for (int i=1;i<=n;i++) 63 for (int j=1;j<=m;j++) 64 insert(i,j+n,R-L),d[i]-=L,d[j+n]+=L,num[i][j]=cnt; 65 for (int i=1;i<=n;i++) 66 { 67 int p=max(0,h[i]-x),q=x+h[i]; 68 insert(s,i,q-p),d[s]-=p,d[i]+=p; 69 } 70 for (int i=1;i<=m;i++) 71 { 72 int p=max(0,l[i]-x),q=x+l[i]; 73 insert(i+n,t,q-p),d[i+n]-=p,d[t]+=p; 74 } 75 int tot=0; 76 for (int i=s;i<=t;i++) if (d[i]>0) insert(S,i,d[i]),tot+=d[i]; else if (d[i]<0) insert(i,T,-d[i]); 77 return dinic()==tot; 78 } 79 int main() 80 { 81 scanf("%d%d",&n,&m); 82 for (int i=1,x;i<=n;i++) for (int j=1;j<=m;j++) scanf("%d",&x),h[i]+=x,l[j]+=x; 83 scanf("%d%d",&L,&R); 84 int l=0,r=200000; 85 while (l<=r) 86 { 87 int mid=l+r>>1; 88 if (check(mid)) r=mid-1; else l=mid+1; 89 } 90 printf("%d",r+1); 91 }