题意:有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 }