POJ 2112 Optimal Milking (二分 + 最大流)
题目大意:
在一个农场里面,有k个挤奶机,编号分别是 1..k,有c头奶牛,编号分别是k+1 .. k+c,每个挤奶机一天最让可以挤m头奶牛的奶,奶牛和挤奶机之间用邻接矩阵给出距离。求让所有奶牛都挤到
奶的情况下,走的最远的那头奶牛走的距离最小是多少。
数据保证有解。
算法讨论:
首先可以想到是二分,然后在选择流网络的时候,一开始选择的最小费用最大流,让二分的边权充当最小费用,但是这样跑发现每次二分的是我们要跑的答案,不可行。所以就改用最大流。
最大流肯定是在二分的情况下判定最大流是否等于c,即是否所有的奶牛都可以挤到奶,所以一开始我的做法是直接把所有的边都加进去,然后在跑bfs的时候把边权大于二分值的边卡掉,但是发现这样是不可行的,(至于为什么不可行,有待思考。。。。)然后另一种做法就是每一次二分的时候都是重新加边(当前是小于二分值的边),然后跑最大流。但是不知道为什么我跑出来那么的效率低下。加边就是这样加:从超级源点向每头牛加流量为1的边,每头牛向距离小于二分当前值的挤奶机加流量为1的边,每个挤奶机向超级汇点加流量为m的边,根据入流等于出流的原理,这样就可以保证每个挤奶机至多只挤m头牛的奶了。
Codes:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cstdlib> 5 #include <algorithm> 6 #include <queue> 7 8 using namespace std; 9 10 int k, c, mm, l, r, Mid; 11 int arr[250][250]; 12 13 struct MF{ 14 static const int N = 230 + 5; 15 static const int M = 300 * 300 + 5; 16 static const int oo = 0x3f3f3f3f; 17 18 int n, m, s, t, tot; 19 int first[N], next[M]; 20 int u[M], v[M], cap[M], flow[M]; 21 int dis[N], cur[N]; 22 bool vi[M]; 23 24 void Clear(){ 25 tot = 0; 26 memset(first, -1, sizeof first); 27 memset(flow, 0 ,sizeof flow); 28 } 29 void Add(int from, int to, int cp, int flw){ 30 u[tot] = from; v[tot] = to; cap[tot] = cp; flow[tot] = flw; 31 next[tot] = first[u[tot]]; 32 first[u[tot]] = tot; 33 ++ tot; 34 } 35 bool bfs(){ 36 memset(vi, false, sizeof vi); 37 38 queue <int> q; 39 dis[s] = 0;vi[s] = true; 40 q.push(s); 41 42 while(!q.empty()){ 43 int now = q.front();q.pop(); 44 for(int i = first[now]; i != -1; i = next[i]){ 45 if(!vi[v[i]] && cap[i] > flow[i]){ 46 vi[v[i]] = true; 47 dis[v[i]] = dis[now] + 1; 48 q.push(v[i]); 49 } 50 } 51 } 52 return vi[t]; 53 } 54 int dfs(int x, int a){ 55 if(x == t || a == 0) return a; 56 int flw = 0, f; 57 int &i = cur[x]; 58 for(i = first[x]; i != -1; i = next[i]){ 59 if(dis[x] + 1 == dis[v[i]] && (f = dfs(v[i], min(a, cap[i]-flow[i]))) > 0){ 60 flow[i] += f; flow[i^1] -= f; 61 a -= f; flw += f; 62 if(a == 0) break; 63 } 64 } 65 return flw; 66 } 67 int MaxFlow(int s, int t){ 68 this->s = s;this->t = t; 69 int flw = 0; 70 while(bfs()){ 71 memset(cur, 0, sizeof cur); 72 flw += dfs(s, oo); 73 } 74 return flw; 75 } 76 }Net; 77 78 void Floyed(){ 79 for(int kk = 1; kk <= k + c; ++ kk) 80 for(int i = 1; i <= k + c; ++ i) 81 for(int j = 1; j <= k + c; ++ j) 82 if(kk != i && i != j && kk != j) 83 if(arr[i][kk] < 1000000 && arr[kk][j] < 1000000) 84 arr[i][j] = min(arr[i][j], arr[i][kk] + arr[kk][j]); 85 } 86 87 bool check(){ 88 Net.Clear(); 89 for(int i = 1; i <= c; ++ i){ 90 Net.Add(0, i + k, 1, 0); 91 Net.Add(i + k, 0, 0, 0); 92 } 93 for(int i = 1; i <= k; ++ i){ 94 Net.Add(i, Net.n + 1, mm, 0); 95 Net.Add(Net.n + 1, i, 0, 0); 96 } 97 for(int i = k + 1; i <= k + c; ++ i){ 98 for(int j = 1; j <= k; ++ j){ 99 if(arr[i][j] <= Mid){ 100 Net.Add(i, j, 1, 0); 101 Net.Add(j, i, 0, 0); 102 } 103 } 104 } 105 if(Net.MaxFlow(0, Net.n + 1) == c) return true; 106 return false; 107 } 108 109 void Solve(){ 110 int ans=0;l = 1;r = 10000; 111 while(l <= r){ 112 Mid = l + (r - l) / 2; 113 if(check()){ 114 ans = Mid;r = Mid - 1; 115 } 116 else l = Mid + 1; 117 } 118 printf("%d\n", ans); 119 } 120 int main(){ 121 122 scanf("%d%d%d", &k, &c, &mm); 123 Net.n = k + c; 124 memset(arr, 127/3, sizeof arr); 125 for(int i = 1; i <= k + c; ++ i) 126 for(int j = 1; j <= k + c; ++ j){ 127 scanf("%d", &arr[i][j]); 128 arr[i][j] = arr[i][j] == 0 ? Net.oo : arr[i][j]; 129 } 130 Floyed(); 131 Solve(); 132 return 0; 133 }