POJ-2112 Optimal Milking 二分+网络流
题意:
有K个挤奶站,C头奶牛,每个挤奶站每天最多只能够为M头奶牛服务,奶牛到奶牛,奶牛到挤奶站都有一个距离,问一天能够为所有的奶牛都挤奶的匹配方案中,选择最远的距离最小方案。
解法:
通过二分枚举来构建边的关系,然后用网络流求解是否所有的牛在满足条件的情况下是否都能够被挤奶。
代码如下:
#include <cstdlib> #include <cstring> #include <cstdio> #include <algorithm> #define inf 0x3fffffff using namespace std; int K, C, M, idx; int G[250][250], head[250], SS = 240, TT = 241; int dis[250], que[250], front, tail; struct Node { int v, next, cap; }e[60000]; void Floyd() { for (int k = 1; k <= K+C; ++k) { for (int i = 1; i <= K+C; ++i) { for (int j = 1; j <= K+C; ++j) { if (G[i][k] != -1 && G[k][j] != -1) { if (G[i][k] + G[k][j] < G[i][j] || G[i][j] == -1) { // 如果某一点为-1的话,应该是直接更新的,这里被坑了 G[i][j] = G[i][k] + G[k][j]; } } } } } /* for (int i = K+1; i <= K+C; ++i) { for (int j = 1; j <= K; ++j) { printf("%d %d = %d\n", i, j, G[i][j]); } } system("pause");*/ } void addedge(int x, int v, int cap) { ++idx; e[idx].v = v, e[idx].cap = cap; e[idx].next = head[x]; head[x] = idx; } bool bfs() { int pos; memset(dis, 0xff, sizeof (dis)); dis[SS] = 0; front = tail = 0; que[++tail] = SS; while (front != tail) { pos = que[++front]; for (int i = head[pos]; i != -1; i = e[i].next) { int v = e[i].v, cap = e[i].cap; if (dis[v] == -1 && cap > 0) { dis[v] = dis[pos] + 1; que[++tail] = v; } } } return dis[TT] != -1; } int dfs(int u, int flow) { if (u == TT) { return flow; } int tf = 0, f; for (int i = head[u]; i != -1; i = e[i].next) { int v = e[i].v, cap = e[i].cap; if (dis[v] == dis[u]+1 && cap > 0 && (f = dfs(v, min(cap, flow)))) { e[i].cap -= f, e[i^1].cap += f; flow -= f; tf += f; } } if (tf == 0) dis[u] = -1; return tf; } int Dinic() { int ret = 0; while (bfs()) { ret += dfs(SS, inf); } return ret; } int bsearch(int l, int r) { int ret; while (l <= r) { int mid = (l + r) >> 1; memset(head, 0xff, sizeof (head)); idx = -1; for (int i = 1; i <= K; ++i) { // 遍历所有的挤奶器 addedge(i, TT, M);// 边的容量就是其最大的限制量 addedge(TT, i, 0); } for (int i = K+1; i <= K+C; ++i) { // 遍历所有的奶牛 addedge(SS, i, 1); addedge(i, SS, 0); for (int j = 1; j <= K; ++j) { // 枚举每头奶牛到挤奶器的距离 if (G[i][j] != -1 && G[i][j] <= mid) { addedge(i, j, 1); addedge(j, i, 0); } } } if (Dinic() == C) { ret = mid; r = mid - 1; } else { l = mid + 1; } } return ret; } int main() { while (scanf("%d %d %d", &K, &C, &M) == 3) { // 前K个是挤奶站,后C个奶牛所在位置 memset(G, 0xff, sizeof (G)); for (int i = 1; i <= K+C; ++i) { for (int j = 1; j <= K+C; ++j) { scanf("%d", &G[i][j]); if (G[i][j] == 0) G[i][j] = -1; } } Floyd(); // 右边界不是200,因为可能有点边是通过其他边传递过去的 printf("%d\n", bsearch(1, 50000)); } return 0; }