POJ2112 最大流(Isap+Floyd+二分)

题意:有K个挤奶器,C头奶牛,每个挤奶器最多能给M头奶牛挤奶。求使C头奶牛头奶牛需要走的路程的最大路程最小。

(1)首先跑一遍Floyd,求出任意两个点的最短距离

(2)然后进行二分,对奶牛和挤奶器距离<=mid的建边,权值为1

(3)判断是否C头奶牛都可以有挤奶器挤奶,(最大流是否等于C)

(4)然后一直二分求最小的

(5)要注意的地方,代码有注释

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <cstdlib>
  5 #include <algorithm>
  6 using namespace std;
  7 #define inf 0x7fffffff
  8 #define N 300
  9 #define M 40000
 10 int dis[N],gap[N],cur[N],pre[N];
 11 int size,head[N];
 12 int map[N][N];
 13 int num,k,c,m;
 14 int s,e,nv,mid;
 15 struct Edge {
 16     int v,w,next;
 17     Edge() {}
 18     Edge(int V,int W,int NEXT):v(V),w(W),next(NEXT) {}
 19 } edge[M];
 20 void InsertEdge(int u,int v,int w) { // 加边 
 21     edge[size] = Edge(v,w,head[u]);
 22     head[u] = size++;
 23     edge[size] = Edge(u,0,head[v]);
 24     head[v] = size++;
 25 }
 26 int Isap(int st,int ed,int n) { //Isap模板 
 27     for(int i=0; i<=n; i++) {
 28         cur[i] = head[i];
 29         gap[i] = dis[i] = 0;
 30     }
 31     int u = pre[st] = st;
 32     int aug = inf ,maxflow = 0;
 33     while(dis[st] < n) {
 34 loop:
 35         for(int &i=cur[u]; i!=-1; i=edge[i].next) {
 36             int v = edge[i].v;
 37             if(edge[i].w && dis[u] == dis[v] + 1) {
 38                 aug = min(aug,edge[i].w);
 39                 pre[v] = u;
 40                 u = v;
 41                 if(v==ed) {
 42                     maxflow += aug;
 43                     for(u=pre[u]; v!=st; v=u,u=pre[u]) {
 44                         edge[cur[u]].w -= aug;
 45                         edge[cur[ u]^1].w += aug;
 46                     }
 47                     aug = inf;
 48                 }
 49                 goto loop;
 50             }
 51         }
 52         int mindis = n;
 53         for(int i=head[u]; i!=-1; i=edge[i].next) {
 54             int v = edge[i].v;
 55             if(edge[i].w && dis[v]<mindis) {
 56                 cur[u] = i;
 57                 mindis = dis[v];
 58             }
 59         }
 60         if(--gap[dis[u]]==0) break;
 61         gap[dis[u] = mindis+1]++;
 62         u = pre[u];
 63     }
 64     return maxflow;
 65 }
 66 
 67 void Init() {    // 建图 
 68     memset(head,-1,sizeof(head));
 69     size = 0;
 70     for(int i=1; i<=k; i++) { //k个挤奶器 
 71         for(int j=k+1; j<=num; j++) { //c头奶牛 
 72             if(map[i][j]<=mid) InsertEdge(j,i,1); //如果奶牛到挤奶器的最短距离<=mid,建权值为1的边 
 73         }
 74     }
 75     for(int i=1; i<=k; i++) InsertEdge(i,e,m); //每个挤奶器最多可以挤k头牛 
 76     for(int i=k+1; i<=num; i++) InsertEdge(s,i,1); //建一条源点到奶牛的边,权值为1 
 77 
 78 }
 79 
 80 void Floyd() { //Floyd求两个点的最短距离 
 81     for(int pos=1; pos<=num; pos++) {
 82         for(int i=1; i<=num; i++) {
 83             if(pos==i||map[i][pos]==inf) continue;
 84             for(int j=1; j<=num; j++) {
 85                 if(pos==j||map[pos][j]==inf) continue;
 86                 if(map[i][j] > map[i][pos] + map[pos][j])
 87                     map[i][j] = map[i][pos] + map[pos][j];
 88             }
 89         }
 90     }
 91 }
 92 
 93 int main() {
 94     while(scanf("%d%d%d",&k,&c,&m)!=EOF) {
 95         num = k+c; //矩阵的规格 
 96         s = 0 ; //源点 
 97         e = num+1; //汇点 
 98         nv = e+1; //结点总数 
 99         for(int i=1; i<=num; i++) {
100             for(int j=1; j<=num; j++) {
101                 scanf("%d",&map[i][j]);
102                 if(i!=j && !map[i][j]) //除了i==j,map[i][j]==0,map[i][j]=inf;
103                     map[i][j] = inf;
104             }
105         }
106         Floyd();
107         int l = 0 , r = 40000;
108         while(l<=r) {
109             mid=l+(r-l)/2; //有时候l+r可以超了。。 
110             Init();
111             if(Isap(s,e,nv)==c) r = mid-1; //如果所有的奶牛都可以找到相应的挤奶器 
112             else l = mid+1;
113         }
114         printf("%d
",l);
115     }
116     return 0;
117 }
View Code
原文地址:https://www.cnblogs.com/ar940507/p/3264877.html