Optimal Milking poj2112 二分+最大流

本文介绍了一种利用二分查找法与最短路径算法解决特定问题的方法:即如何在满足条件的情况下使奶牛到产奶机的最远距离最小。通过Floyd算法预处理最短路径,并结合Dinic最大流算法实现有效的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description


K个产奶机,C头奶牛,且每个产奶机最多可供M头奶牛使用;已知奶牛之间的两两距离,求如何安排使得在任何一头奶牛都有自己产奶机的条件下,奶牛到产奶机的最远距离最短?最短是多少?

Solution


看到最大值最小就要想到二分答案了

先floyd求两两之间最短路,二分的时候上界直接粗暴地用INF,或者别的什么也行

机器和牛棚分别连向源点和汇点,其中机器的限制m作为源点连向机器的容量

Code


#include <stdio.h>
#include <string.h>
#include <queue>
#define rep(i, st, ed) for (int i = st; i <= ed; i += 1)
#define erg(i, now) for (int i = ls[now]; i; i = e[i].next)
#define fill(x, t) memset(x, t, sizeof(x))
#define INF 0x3f3f3f3f
#define N 531
#define E N * N / 2 + 1
struct edge{int x, y, w, next;}e[E];
int rc[N][N], dis[N], ls[N];
inline void addEdge(int &cnt, int x, int y, int w){
    e[++ cnt] = (edge){x, y, w, ls[x]}; ls[x] = cnt;
    e[++ cnt] = (edge){y, x, 0, ls[y]}; ls[y] = cnt;
}
using std:: queue;
inline int bfs(int st, int ed){
    fill(dis, -1);
    dis[st] = 1;
    queue<int> q;
    q.push(st);
    while (!q.empty()){
        int now = q.front(); q.pop();
        erg(i, now){
            if (e[i].w > 0 && dis[e[i].y] == -1){
                q.push(e[i].y);
                dis[e[i].y] = dis[now] + 1;
                if (e[i].y == ed){
                    return 1;
                }
            }
        }
    }
    return 0;
}
inline int min(int x, int y){
    return x<y?x:y;
}
inline int find(int now, int ed, int mn){
    if (now == ed || !mn){
        return mn;
    }
    int ret = 0;
    erg(i, now){
        if (e[i].w > 0 && dis[now] + 1 == dis[e[i].y]){
            int d = find(e[i].y, ed, min(mn - ret, e[i].w));
            e[i].w -= d;
            e[i ^ 1].w += d;
            ret += d;
            if (ret == mn){
                break;
            }
        }
    }
    return ret;
}
inline int dinic(int st, int ed){
    int mxFlow = 0;
    while (bfs(st, ed)){
        mxFlow += find(st, ed, INF);
    }
    return mxFlow;
}
inline int cheque(int lim, int k, int c, int m){
    int st = 0, ed = N - 1;
    int edgeCnt = 1;
    fill(ls, 0);
    rep(i, 1, k){
        rep(j, k + 1, k + c){
            if (rc[i][j] <= lim){
                addEdge(edgeCnt, i, j, 1);
            }
        }
    }
    rep(i, 1, k){
        addEdge(edgeCnt, st, i, m);
    }
    rep(i, k + 1, k + c){
        addEdge(edgeCnt, i, ed, 1);
    }
    int mxFlow = dinic(st, ed);
    return mxFlow;
}
int main(void){
    int p, c, m;
    scanf("%d%d%d", &p, &c, &m);
    rep(i, 1, p + c){
        rep(j, 1, p + c){
            scanf("%d", &rc[i][j]);
            if (!rc[i][j]){
                rc[i][j] = INF;
            }
        }
    }
    rep(k, 1, p + c){
        rep(i, 1, p + c){
            rep(j, 1, p + c){
                if (i != j && j != k && i != k && rc[i][k] + rc[k][j] < rc[i][j]){
                    rc[i][j] = rc[i][k] + rc[k][j];
                }
            }
        }
    }
    int l = 0, r = INF - 1;
    int ans = 0;
    while (l <= r){
        int mid = (l + r) >> 1;
        int mxFlow = cheque(mid, p, c, m);
        if (mxFlow >= c){
            ans = mid;
            r = mid - 1;
        }else{
            l = mid + 1;
        }
    }
    printf("%d\n", ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值