poj 2112 Optimal Milking 【网络流】

Problem:
有k个机器和c头奶牛,一个机器最多可以供应m个奶牛使用,给定奶牛和机器之间的路径(图),问走的最远的奶牛走的路最短有多远?
Solution:
这道题难在不知道是网络流的题目,首先需要转换模型,设立一个超级起点向每一个奶牛的容量设置为1,然后超级汇点,每一个机器向汇点容量为m,题意就变成了在最大流是c的情况下距离如何最短?此时我们利用二分搜索,找到满足最大流是c的情况下的最逼近的距离。利用dinic网络流即可实现。
note:
1. 算法模板的效率一定要高,这样才能保证上层封装的效率,利用bfs模拟的dinic明显不如现在的直接dfs效率高。
2. 要对模型有一定的敏感性。

#include<cstdio>
#include<iostream>
#include<sstream>
#include<cstdlib>
#include<cmath>
#include<cctype>
#include<string>
#include<cstring>
#include<algorithm>
#include<stack>
#include<queue>
#include<set>
#include<map>
#include<ctime>
#include<vector>
#include<fstream>
#include<list>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
#define ms(s) memset(s,0,sizeof(s))

const double PI = 3.141592653589;
const int INF = 0x3fffffff;

int G[240][240];
int k, c, m;
int n;

void floyd(){
    for(int k = 1; k <= n; ++k)
        for(int i = 1; i <= n; ++i)
            for(int j = 1; j <= n; ++j)
                G[i][j] = min(G[i][j], G[i][k]+G[k][j]);
}

const int maxn = 240;
int cap[maxn][maxn];
int layer[maxn];
bool visited[maxn];
int st, en;//start point, end point

bool count_layer() {
    queue<int> q;  q.push(st);
    memset(layer, -1, sizeof(layer));
    layer[st] = 0;

    while(!q.empty()) {
        int v = q.front();  q.pop();
        for(int i = st; i <= en; i++) {
            if(layer[i] == -1 && cap[v][i] > 0) {
                layer[i] = layer[v]+1;
                if(i == en)
                    return true;
                q.push(i);
            }
        }
    }
    return false;
}

int dfs(int root, int cur_flow) {
    int dt = cur_flow;//the rest of cur_flow
    if (root == en) return dt;
    for (int i = 0; i <= en; i++) {
        if (cap[root][i] > 0 && layer[i] == layer[root]+1) {
            int flow = dfs(i, min(dt, cap[root][i]));
            cap[root][i] -= flow;
            cap[i][root] += flow;
            dt -= flow;
        }
    }
    return cur_flow - dt;
}

int dinic() {
    int ans = 0;
    while(count_layer())
        ans += dfs(st, INF);
    return ans;
}

void reG(int w) {
    ms(cap);
    for(int i = k+1; i <= k+c; i++) {
        for(int j = 1; j <= k; j++) {
            cap[i][j] = (G[i][j] <= w) ? 1 : 0;
        }
    }
    for(int i = k+1; i <= k+c; i++)
        cap[0][i] = 1;
    for(int i = 1; i <= k; i++)
        cap[i][n+1] = m;
}

int main() {
//        freopen("/Users/really/Documents/code/input","r",stdin);
    //    freopen("/Users/really/Documents/code/output","w",stdout);
    ios::sync_with_stdio(false);

    cin >> k >> c >> m;
    n = k + c;
    en = n+1;
    st = 0;
    int w;
    for(int i = 1; i <= k+c; i++) {
        for(int j = 1; j <= k+c; j++) {
            cin >> w;
            G[i][j] = (w==0) ? INF : w;
        }
    }

    floyd();

    int l = 1, r = 40000;
    int mid;

    while(l < r) {
        mid = (l+r)/2;
        reG(mid);
        int t = dinic();
        if(t == c)
            r = mid;
        else
            l = mid+1;
    }
    cout << l << endl;

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值