[上下界网络流][二分] Bzoj P2406 矩阵

题目描述

输入输出格式

输入格式:

 

第一行两个数n、m,表示矩阵的大小。

接下来n行,每行m列,描述矩阵A。

最后一行两个数L,R。

 

输出格式:

 

第一行,输出最小的答案;

 

输入输出样例

输入样例#1:
2 2
0 1
2 1
0 1
输出样例#1:
1

说明

对于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 }

 

posted @ 2019-01-22 15:36  BEYang_Z  阅读(237)  评论(1编辑  收藏  举报