POJ2112 Optimal Milking---二分+Floyd+网络流

题目链接:

https://vjudge.net/problem/POJ-2112

题目大意:

k个机器,每个机器最多服务m头牛。

c头牛,每个牛需要1台机器来服务。

告诉你牛与机器每个之间的直接距离。

问:让所有的牛都被服务的情况下,使走的最远的牛的距离最短,求这个距离。

解题思路:

二分枚举距离,实际距离满足当前枚举距离限制的可以加入这条边。枚举的距离中符合条件的最小值就是答案。

建图过程:

一个超级汇点,每个机器和汇点的容量都是m。

一个超级源点,和每头牛的容量都是1.

机器i与牛j之间的距离如果小于等于当前枚举值mid,连接i,j,容量1.

这样最大流的意义就是能够服务的牛最多是多少,如果最大流等于牛的总数c,表示当前枚举值mid符合条件,同时说明mid值还可能可以更小,更新二分右边界r = mid .(最终答案也是r不是mid)

如果小于牛的总数,说明mid偏小,更新二分左边界,l = mid + 1.

机器与牛之间的最短距离可以用floyd预处理出来。

  1 #include<iostream>
  2 #include<vector>
  3 #include<cstring>
  4 #include<queue>
  5 using namespace std;
  6 const int maxn = 300 + 10;
  7 const int INF = 0x3f3f3f3f;
  8 struct edge
  9 {
 10     int u, v, c, f;
 11     edge(int u, int v, int c, int f):u(u), v(v), c(c), f(f){}
 12 };
 13 vector<edge>e;
 14 vector<int>G[maxn];
 15 int a[maxn], p[maxn];
 16 void init(int n)
 17 {
 18     e.clear();
 19     for(int i = 0; i <= n; i++)G[i].clear();
 20 }
 21 void addedge(int u, int v, int c)
 22 {
 23     //cout<<u<<" "<<v<<" "<<c<<endl;
 24     e.push_back(edge(u, v, c, 0));
 25     e.push_back(edge(v, u, 0, 0));
 26     int m = e.size();
 27     G[u].push_back(m - 2);
 28     G[v].push_back(m - 1);
 29 }
 30 int Maxflow(int s, int t)
 31 {
 32     int flow = 0;
 33     for(;;)
 34     {
 35         memset(a, 0, sizeof(a));
 36         queue<int>q;
 37         q.push(s);
 38         a[s]  =INF;
 39         while(!q.empty())
 40         {
 41             int u = q.front();
 42             q.pop();
 43             for(int i = 0; i < G[u].size(); i++)
 44             {
 45                 edge& now = e[G[u][i]];
 46                 int v = now.v;
 47                 if(!a[v] && now.c > now.f)//还未流经并且边还有容量
 48                 {
 49                     p[v] = G[u][i];
 50                     a[v] = min(a[u], now.c - now.f);
 51                     q.push(v);
 52                 }
 53             }
 54             if(a[t])break;//已经到达汇点
 55         }
 56         if(!a[t])break;//已经没有增广路
 57         for(int u = t; u != s; u = e[p[u]].u)
 58         {
 59             e[p[u]].f += a[t];
 60             e[p[u] ^ 1].f -= a[t];
 61         }
 62         flow += a[t];
 63     }
 64     return flow;
 65 }
 66 int k, c, m, n;
 67 int s, t;
 68 int Map[maxn][maxn];
 69 void build_map(int Maxdist_min)
 70 {
 71     init(n);//每次构建容量网络清空边
 72     //超级源点s和所有牛建边,权值为1
 73     for(int i = k + 1; i <= n; i++)addedge(s, i, 1);
 74     //所有挤奶机和超级汇点建边,权值为m(也就是一台挤奶机可供牛的最大数目)
 75     for(int i = 1; i <= k; i++)addedge(i, t, m);
 76     for(int i = k + 1; i <= n; i++)//牛的编号,后c个
 77     {
 78         for(int j = 1; j <= k; j++)//挤奶机编号,前k个
 79         {
 80             if(Map[i][j] <= Maxdist_min)//小于最大距离,那么可以直达
 81                 addedge(i, j, 1);//牛和挤奶机可以配对
 82         }
 83     }
 84 }
 85 int main()
 86 {
 87     cin >> k >> c >> m;
 88     n = k + c;
 89     for(int i = 1; i <= n; i++)//前k个点为挤奶机,后c个点为牛
 90     {
 91         for(int j = 1; j <= n; j++)
 92         {
 93             cin >> Map[i][j];
 94             if(Map[i][j] == 0)Map[i][j] = INF;
 95         }
 96     }
 97     //Floyd求最短路
 98     for(int k = 1; k <= n; k++)
 99     {
100         for(int i = 1; i <= n; i++)
101         {
102             for(int j = 1; j <= n; j++)
103             {
104                 Map[i][j] = min(Map[i][j], Map[i][k] + Map[k][j]);
105             }
106         }
107     }
108     int l = 0, r = 10000, mid;//此处最大距离不能是200,题目说的200只是一条边,可能要走很多条边
109     s = 0, t = n + 1;
110     while(l < r)
111     {
112         mid = (l + r) / 2;
113         //cout<<mid<<endl;
114         build_map(mid);
115         if(Maxflow(s, t) >= c)//说明所有的牛已经到达,最大距离可以更小
116             r = mid;
117         else l = mid + 1;
118     }
119     cout<<r<<endl;//最大距离最小,这里的最大距离是r不是mid,因为最后一次循环的时候可能只更新mid和l,没有更新r
120 }
原文地址:https://www.cnblogs.com/fzl194/p/8870772.html