【上下界网络流 二分】bzoj2406: 矩阵
感觉考试碰到上下界网络流也还是写不来啊
Description
Input
第一行两个数n、m,表示矩阵的大小。
接下来n行,每行m列,描述矩阵A。
最后一行两个数L,R。
Output
第一行,输出最小的答案;
HINT
对于100%的数据满足N,M<=200,0<=L<=R<=1000,0<=Aij<=1000
题目分析
首先二分行列之差的最大值。
这一类行列上的问题,属于经典的网络流模型。将行列各自看成点,由S向这些点连容量为$[ΣA_{i,j}-x,ΣA_{i,j}+x]$的边,再在行列之间互相连$[L,R]$的边,那么最终每个点的流量就是其行/列的权值和。
于是问题就变成了判定有源汇上下界可行流。
1 #include<bits/stdc++.h> 2 const int maxn = 435; 3 const int maxm = 500035; 4 const int INF = 2e9; 5 6 struct Edge 7 { 8 int u,v,f,c; 9 Edge(int a=0, int b=0, int c=0, int d=0):u(a),v(b),f(c),c(d) {} 10 }edges[maxm]; 11 int edgeTot,head[maxn],nxt[maxm],lv[maxn],cur[maxn]; 12 int r[maxn],c[maxn],a[maxn][maxn]; 13 int n,m,S,T,SS,TT,lLim,rLim,ans; 14 15 int read() 16 { 17 char ch = getchar(); 18 int num = 0, fl = 1; 19 for (; !isdigit(ch); ch=getchar()) 20 if (ch=='-') fl = -1; 21 for (; isdigit(ch); ch=getchar()) 22 num = (num<<1)+(num<<3)+ch-48; 23 return num*fl; 24 } 25 void addedge(int u, int v, int c) 26 { 27 edges[edgeTot] = Edge(u, v, 0, c), nxt[edgeTot] = head[u], head[u] = edgeTot++; 28 edges[edgeTot] = Edge(v, u, 0, 0), nxt[edgeTot] = head[v], head[v] = edgeTot++; 29 } 30 bool buildLevel() 31 { 32 std::queue<int> q; 33 memset(lv, 0, sizeof lv); 34 lv[S] = 1, q.push(S); 35 for (int i=1; i<=TT; i++) cur[i] = head[i]; 36 for (int tmp; q.size(); ) 37 { 38 tmp = q.front(), q.pop(); 39 for (int i=head[tmp]; i!=-1; i=nxt[i]) 40 { 41 int v = edges[i].v; 42 if (!lv[v]&&edges[i].f < edges[i].c){ 43 lv[v] = lv[tmp]+1, q.push(v); 44 if (v==T) return true; 45 } 46 } 47 } 48 return false; 49 } 50 int fndPath(int x, int lim) 51 { 52 if (x==T) return lim; 53 for (int &i=cur[x]; i!=-1; i=nxt[i]) 54 { 55 int v = edges[i].v, val; 56 if (lv[x]+1==lv[v]&&edges[i].f < edges[i].c){ 57 if ((val = fndPath(v, std::min(lim, edges[i].c-edges[i].f)))){ 58 edges[i].f += val, edges[i^1].f -= val; 59 return val; 60 }else lv[v] = -1; 61 } 62 } 63 cur[x] = head[x]; 64 return 0; 65 } 66 int dinic() 67 { 68 int ret = 0, val; 69 while (buildLevel()) 70 while ((val = fndPath(S, INF))) ret += val; 71 return ret; 72 } 73 bool check(int x) 74 { 75 int cur = 0; 76 memset(head, -1, sizeof head); 77 edgeTot = 0; 78 for (int i=1; i<=n; i++) 79 { 80 if (r[i]+x < 0) return false; 81 if (r[i]-x > 0){ 82 addedge(SS, T, r[i]-x); 83 addedge(S, i, r[i]-x); 84 addedge(SS, i, x<<1); 85 cur += r[i]-x; 86 }else addedge(SS, i, r[i]+x); 87 } 88 for (int i=1; i<=m; i++) 89 { 90 if (c[i]+x < 0) return false; 91 if (c[i]-x > 0){ 92 addedge(S, TT, c[i]-x); 93 addedge(i+n, T, c[i]-x); 94 addedge(i+n, TT, x<<1); 95 cur += c[i]-x; 96 }else addedge(i+n, TT, c[i]+x); 97 } 98 for (int i=1; i<=n; i++) 99 for (int j=1; j<=m; j++) 100 addedge(i, j+n, rLim); 101 addedge(TT, SS, INF); 102 return cur==dinic(); 103 } 104 int main() 105 { 106 n = read(), m = read(); 107 S = n+m+1, T = S+1, SS = T+1, TT = SS+1; 108 for (int i=1; i<=n; i++) 109 for (int j=1; j<=m; j++) 110 a[i][j] = read(); 111 lLim = read(), rLim = read(); 112 for (int i=1; i<=n; i++) 113 for (int j=1; j<=m; j++) 114 a[i][j] -= lLim, r[i] += a[i][j], c[j] += a[i][j]; 115 rLim -= lLim; 116 int L = 0, R = std::max(*std::max_element(r+1, r+n+1), *std::max_element(c+1, c+m+1)); 117 for (int mid=(L+R)>>1; L<=R; mid=(L+R)>>1) 118 if (check(mid)) ans = mid, R = mid-1; 119 else L = mid+1; 120 printf("%d\n",ans); 121 return 0; 122 }
END