POJ 2112 Optimal Milking 最优挤奶方案 Floyd算法+二分查找+最大流

题目链接:POJ 2112 Optimal Milking


Optimal Milking

Time Limit: 2000MS   Memory Limit: 30000K
Total Submissions: 12446   Accepted: 4494
Case Time Limit: 1000MS

Description

FJ has moved his K (1 <= K <= 30) milking machines out into the cow pastures among the C (1 <= C <= 200) cows. A set of paths of various lengths runs among the cows and the milking machines. The milking machine locations are named by ID numbers 1..K; the cow
locations are named by ID numbers K+1..K+C.

Each milking point can "process" at most M (1 <= M <= 15) cows each day.

Write a program to find an assignment for each cow to some milking machine so that the distance the furthest-walking cow travels is minimized (and, of course, the milking machines are not overutilized). At least one legal assignment is possible for all input
data sets. Cows can traverse several paths on the way to their milking machine.

Input

* Line 1: A single line with three space-separated integers: K, C, and M.

* Lines 2.. ...: Each of these K+C lines of K+C space-separated integers describes the distances between pairs of various entities. The input forms a symmetric matrix. Line 2 tells the distances from milking machine 1 to each of the other entities; line 3 tells
the distances from machine 2 to each of the other entities, and so on. Distances of entities directly connected by a path are positive integers no larger than 200. Entities not directly connected by a path have a distance of 0. The distance from an entity
to itself (i.e., all numbers on the diagonal) is also given as 0. To keep the input lines of reasonable length, when K+C > 15, a row is broken into successive lines of 15 numbers and a potentially shorter line to finish up a row. Each new row begins on its
own line.

Output

A single line with a single integer that is the minimum possible total distance for the furthest walking cow.

Sample Input

2 3 2
0 3 2 1 1
3 0 3 2 0
2 3 0 1 0
1 2 1 0 2
1 0 0 2 0

Sample Output

2

Source

USACO 2003 U S Open

题意:

K个挤奶器(编号1~K),每个每天最多挤M头奶牛。共有C头奶牛(编号K+1~K+C)。挤奶器和奶牛间有不同长度的路。

编写程序,寻找一个方案,安排每头牛到某个挤奶器挤奶,并使得C头奶牛需要走的所有路程的最大路程最小。

分析:

因为有多个挤奶器和多头奶牛,因而要求最短路需要Floyd。然后用最大流算法求解;搜索最大距离的最小值采用二分枚举。

关键问题是构图:

构图思路:

(1)以0为源点s,n+1为汇点t。

(2)s向每头奶牛引一条边,容量为1,代表s走向每头奶牛的最大奶牛数。

(3)每个挤奶器向t引一条边,容量为M,代表每个挤奶器最多向t走M头奶牛。

(4)对于每头奶牛通向每个挤奶器的最短路,如果小于枚举当前距离,说明能够走到,那么从奶牛到挤奶器引一条边,容量为1。

(5)构图完毕。

最大流:

最大流可以用三种方法实现:

(1)EK,普通的SAP算法,因要不断BFS,复杂度较高:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
using namespace std;

#define maxn 1010
#define INF 0x3f3f3f3f
int s, t, n, K, C, M;
int a[maxn], pre[maxn];
int flow[maxn][maxn];
int cap[maxn][maxn], mp[maxn][maxn];

int EK()
{
    queue<int> q;
    memset(flow, 0, sizeof(flow));
    int ans = 0;
    while(1)
    {
        memset(a, 0, sizeof(a));
        a[s] = INF;
        q.push(s);
        while(!q.empty())   //bfs找增广路径
        {
            int u = q.front();
            q.pop();
            for(int v = 1; v <= n; v++)
                if(!a[v] && cap[u][v] > flow[u][v])
                {
                    pre[v] = u;
                    q.push(v);
                    a[v] = min(a[u], cap[u][v]-flow[u][v]);
                }
        }
        if(a[t] == 0) break;
        for(int u = t; u != s; u = pre[u])  //改进网络流
        {
            flow[pre[u]][u] += a[t];
            flow[u][pre[u]] -= a[t];
        }
        ans += a[t];
    }
    return ans;
}
void Floyd()
{
    for(int k = 1; k <= n; k++)
        for(int i = 1; i <= n; i++) {
            if(mp[i][k] != INF)
                for(int j = 1; j <= n; j++)
                    mp[i][j] = min(mp[i][j], mp[i][k]+mp[k][j]);
        }
}
void build(int mid)
{
    n = K+C;
    memset(cap, 0, sizeof(cap));
    for(int i = K+1; i <= n; i++)
        cap[s][i] = 1;
    for(int i = 1; i <= K; i++)
        cap[i][t] = M;
    for(int i = K+1; i <= n; i++)
        for(int j = 1; j <= K; j++)
            if(mp[i][j] <= mid)
                cap[i][j] = 1;
    n = K+C+2;
}
void Solve()
{
    s = 0, t = n+1;
    int l = 0, r = 10000;
    while(l < r)
    {
        int mid = (l+r)/2;
        build(mid);
        int ans = EK();
        if(ans >= C) r = mid;
        else l = mid+1;
    }
    printf("%d\n", r);
}
int main()
{
    //freopen("poj_2112.txt", "r", stdin);
    while(~scanf("%d%d%d", &K, &C, &M)) {
        n = K+C;
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= n; j++) {
                scanf("%d", &mp[i][j]);
                if(mp[i][j] == 0) mp[i][j] = INF;
            }
    Floyd();
    Solve();
    }
    return 0;
}

(2)Dinic,SAP的一种优化方案,保存层次网络,用DFS增广更加快速。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
using namespace std;

#define maxn 250
#define INF 0x3f3f3f3f

struct Edge
{
    int from, to, cap;
};

vector<Edge> EG;
vector<int> G[maxn];
int n, s, t, d[maxn], cur[maxn], mp[maxn][maxn];
int K, C, M;
void addEdge(int from, int to, int cap)
{
    EG.push_back((Edge){from, to, cap});
    EG.push_back((Edge){to, from, 0});
    int x = EG.size();
    G[from].push_back(x-2);
    G[to].push_back(x-1);
}

bool bfs()
{
    memset(d, -1, sizeof(d));
    queue<int> q;
    q.push(s);
    d[s] = 0;
    while(!q.empty())
    {
        int x = q.front();
        q.pop();
        for(int i = 0; i < G[x].size(); i++)
        {
            Edge& e = EG[G[x][i]];
            if(d[e.to] == -1 && e.cap > 0)
            {
                d[e.to] = d[x]+1;
                q.push(e.to);
            }
        }
    }
    return (d[t]!=-1);
}

int dfs(int x, int a)
{
    if(x == t || a == 0) return a;
    int flow = 0, f;
    for(int& i = cur[x]; i < G[x].size(); i++)
    {
        Edge& e = EG[G[x][i]];
        if(d[x]+1 == d[e.to] && (f = dfs(e.to, min(a, e.cap))) > 0)
        {
            e.cap -= f;
            EG[G[x][i]^1].cap += f;
            flow += f;
            a -= f;
            if(a == 0) break;
        }
    }
    return flow;
}
int Dinic()
{
    int ans = 0;
    while(bfs())
    {
        memset(cur, 0, sizeof(cur));
        ans += dfs(s, INF);
    }
    EG.clear();
    for(int i = 0; i < n; ++i)
        G[i].clear();
    return ans;
}
void Floyd()
{
    for(int k = 1; k <= n; k++)
        for(int i = 1; i <= n; i++) {
            if(mp[i][k] != INF)
                for(int j = 1; j <= n; j++)
                    mp[i][j] = min(mp[i][j], mp[i][k]+mp[k][j]);
        }
}
void build(int mid)
{
    n = K+C;
    for(int i = K+1; i <= n; i++)
        addEdge(s, i, 1);
    for(int i = 1; i <= K; i++)
        addEdge(i, t, M);
    for(int i = K+1; i <= n; i++)
        for(int j = 1; j <= K; j++)
            if(mp[i][j] <= mid)
                addEdge(i, j, 1);
    n = K+C+2;
}
void Solve()
{
    s = 0, t = n+1;
    int l = 0, r = 10000;
    while(l < r)
    {
        int mid = (l+r)/2;
        build(mid);
        int ans = Dinic();
        if(ans >= C) r = mid;
        else l = mid+1;
    }
    printf("%d\n", r);
}
int main()
{
    //freopen("poj_2112.txt", "r", stdin);
    while(~scanf("%d%d%d", &K, &C, &M)) {
        n = K+C;
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= n; j++) {
                scanf("%d", &mp[i][j]);
                if(mp[i][j] == 0) mp[i][j] = INF;
            }
    Floyd();
    Solve();
    }
    return 0;
}

(3)ISAP,改进SAP,更加优化,活用层次网络,只需要一遍BFS,然后进行增广。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
using namespace std;

#define maxn 250
#define INF 0x3f3f3f3f

struct Edge
{
    int from, to, cap, flow;
};

vector<Edge> EG;
vector<int> G[maxn];
int n, s, t, d[maxn], cur[maxn], p[maxn], num[maxn], mp[maxn][maxn];
bool vis[maxn];
int K, C, M;

void addEdge(int from, int to, int cap)
{
    EG.push_back((Edge){from, to, cap, 0});
    EG.push_back((Edge){to, from, 0, 0});
    int x = EG.size();
    G[from].push_back(x-2);
    G[to].push_back(x-1);
}

void bfs()
{
    memset(vis, false, sizeof(vis));
    queue<int> q;
    vis[t] = true;
    d[t] = 0;
    q.push(t);
    while(!q.empty()) {
        int x = q.front();
        q.pop();
        for(int i = 0; i < G[x].size(); i++) {
            Edge& e = EG[G[x][i]^1];
            if(!vis[e.from] && e.cap > e.flow) {
                vis[e.from] = true;
                d[e.from] = d[x]+1;
                q.push(e.from);
            }
        }
    }
}

int augment()
{
    int x = t, a = INF;
    while(x != s) {
        Edge& e = EG[p[x]];
        a = min(a, e.cap-e.flow);
        x = EG[p[x]].from;
    }
    x = t;
    while(x != s) {
        EG[p[x]].flow += a;
        EG[p[x]^1].flow -= a;
        x = EG[p[x]].from;
    }
    return a;
}
int ISAP()
{
    int ans =0;
    bfs();
    memset(num, 0, sizeof(num));
    for(int i = 0; i < n; i++)
        num[d[i]]++;
    int x = s;
    memset(cur, 0, sizeof(cur));
    while(d[s] < n) {
        if(x == t) {
            ans += augment();
            x = s;
        }
        bool flag = false;
        for(int i = cur[x]; i < G[x].size(); i++) {
            Edge& e = EG[G[x][i]];
            if(e.cap > e.flow && d[x] == d[e.to]+1) {
                flag = true;
                p[e.to] = G[x][i];
                cur[x] = i;
                x = e.to;
                break;
            }
        }
        if(!flag) {
            int m = n-1;
            for(int i = 0; i < G[x].size(); i++) {
                Edge& e = EG[G[x][i]];
                if(e.cap > e.flow)
                    m = min(m, d[e.to]);
            }
            if(--num[d[x]] == 0) break;
            num[d[x] = m+1]++;
            cur[x] = 0;
            if(x != s)
                x = EG[p[x]].from;
        }
    }
    EG.clear();
    for(int i = 0; i < n; ++i)
        G[i].clear();
    return ans;
}

void Floyd()
{
    for(int k = 1; k <= n; k++)
        for(int i = 1; i <= n; i++) {
            if(mp[i][k] != INF)
                for(int j = 1; j <= n; j++)
                    mp[i][j] = min(mp[i][j], mp[i][k]+mp[k][j]);
        }
}
void build(int mid)
{
    n = K+C;
    for(int i = K+1; i <= n; i++)
        addEdge(s, i, 1);
    for(int i = 1; i <= K; i++)
        addEdge(i, t, M);
    for(int i = K+1; i <= n; i++)
        for(int j = 1; j <= K; j++)
            if(mp[i][j] <= mid)
                addEdge(i, j, 1);
    n = K+C+2;
}
void Solve()
{
    s = 0, t = n+1;
    int l = 0, r = 10000;
    while(l < r)
    {
        int mid = (l+r)/2;
        build(mid);
        int ans = ISAP();
        if(ans >= C) r = mid;
        else l = mid+1;
    }
    printf("%d\n", r);
}
int main()
{
    //freopen("poj_2112.txt", "r", stdin);
    while(~scanf("%d%d%d", &K, &C, &M)) {
        n = K+C;
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= n; j++) {
                scanf("%d", &mp[i][j]);
                if(mp[i][j] == 0) mp[i][j] = INF;
            }
    Floyd();
    Solve();
    }
    return 0;
}

时间比较:

前两个分别为EK和ISAP算法,后面的为Dinic。不知为何我写的ISAP时间要比Dinic总是慢那么一点点,不过能顺利过题目就好了。

可以看出EK效率多差了吧:

Run ID User Problem Result Memory Time Language Code Length Submit Time
13475128 xxx 2112 Accepted 9584K 1875MS G++ 2266B 2014-09-25 02:39:13
13475120 xxx 2112 Accepted 1328K 157MS G++ 3678B 2014-09-25 02:35:15
13475117 xxx 2112 Accepted 1236K 125MS G++ 2812B 2014-09-25 02:33:48
13475113 xxx 2112 Accepted 1236K 141MS G++ 2814B 2014-09-25 02:33:09
13475102 xxx 2112 Accepted 1236K 94MS G++ 2814B 2014-09-25 02:26:43
时间: 2024-12-11 10:32:03

POJ 2112 Optimal Milking 最优挤奶方案 Floyd算法+二分查找+最大流的相关文章

POJ 2112 Optimal Milking (二分 + floyd + 网络流)

POJ 2112 Optimal Milking 链接:http://poj.org/problem?id=2112 题意:农场主John 将他的K(1≤K≤30)个挤奶器运到牧场,在那里有C(1≤C≤200)头奶牛,在奶牛和挤奶器之间有一组不同长度的路.K个挤奶器的位置用1-K的编号标明,奶牛的位置用K+1-K+C 的编号标明.每台挤奶器每天最多能为M(1≤M≤15)头奶牛挤奶.寻找一个方案,安排每头奶牛到某个挤奶器挤奶,并使得C 头奶牛需要走的所有路程中的最大路程最小.每个测试数据中至少有一

POJ 2112 Optimal Milking 二分答案+最大流

首先二分最长的边,然后删去所有比当前枚举的值长的边,算最大流,看是否能满足所有的牛都能找到挤奶的地方 #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <climits> #include <string> #include <iostream> #include <map> #include

POJ 2112 Optimal Milking(二分+最大流)

POJ 2112 Optimal Milking 题目链接 题意:给定一些机器和奶牛,在给定距离矩阵,(不在对角线上为0的值代表不可达),每个机器能容纳m个奶牛,问所有奶牛都能挤上奶,那么走的距离最大的奶牛的最小值是多少 思路:明显的二分+最大流,注意floyd求出的距离矩阵最大值可能不止200,所以二分的上限要注意 代码: #include <cstdio> #include <cstring> #include <queue> #include <algori

POJ 2112—— Optimal Milking——————【多重匹配、二分枚举答案、floyd预处理】

Optimal Milking Time Limit:2000MS     Memory Limit:30000KB     64bit IO Format:%I64d & %I64u Submit Status Practice POJ 2112 Description FJ has moved his K (1 <= K <= 30) milking machines out into the cow pastures among the C (1 <= C <= 20

POJ 2112 Optimal Milking

Optimal Milking Time Limit: 2000ms Memory Limit: 30000KB This problem will be judged on PKU. Original ID: 211264-bit integer IO format: %lld      Java class name: Main FJ has moved his K (1 <= K <= 30) milking machines out into the cow pastures amon

POJ 2112 Optimal Milking (二分 + 最大流)

题目大意: 在一个农场里面,有k个挤奶机,编号分别是 1..k,有c头奶牛,编号分别是k+1 .. k+c,每个挤奶机一天最让可以挤m头奶牛的奶,奶牛和挤奶机之间用邻接矩阵给出距离.求让所有奶牛都挤到 奶的情况下,走的最远的那头奶牛走的距离最小是多少. 数据保证有解. 算法讨论: 首先可以想到是二分,然后在选择流网络的时候,一开始选择的最小费用最大流,让二分的边权充当最小费用,但是这样跑发现每次二分的是我们要跑的答案,不可行.所以就改用最大流. 最大流肯定是在二分的情况下判定最大流是否等于c,即

POJ 2112 Optimal Milking(最大流)

题目链接:http://poj.org/problem?id=2112 Description FJ has moved his K (1 <= K <= 30) milking machines out into the cow pastures among the C (1 <= C <= 200) cows. A set of paths of various lengths runs among the cows and the milking machines. The

POJ 2112.Optimal Milking 解题报告

时间限制:2s 空间限制:30M 题意: 有K台挤奶机(编号1~K),C头奶牛(编号K+1~K+C),给出各点之间距离.现在要让C头奶牛到挤奶机去挤奶,每台挤奶机只能处理M头奶牛,求使所走路程最远的奶牛的路程最短的方案. Solution: 先Floyd求最短路,然后最大流二分答案ans. 若奶牛与挤奶机之间的距离大于ans则不连边,否则连容量为1的边.源向挤奶机连容量M的边,奶牛向汇连容量1的边,用最大流判可行性. code /* 最大流SAP 邻接表 思路:基本源于FF方法,给每个顶点设定层

poj 2112 Optimal Milking 最大流

http://poj.org/problem?id=2112 [题意]  k个机器,每个机器最多服务m头牛.c头牛,每个牛需要1台机器来服务.给出一个矩阵表示每两个实体之间的直接距离.(注意两个实体没有直接 距离的时候用0表示) 问:让所有的牛都被服务的情况下,使走的最远的牛的距离最短,求这个距离. [思路]  先用floyd求出每两个实体间的最短距离,再二分所求的距离len. [建图] 源点向每个机器连边,容量为m.每个机器向距离小于len的牛连边,容量为1.每头牛向汇点连边,容量为1.若最大