POJ 2112 Optimal Milking(最大流+二分枚举答案)

博客讲述了如何使用最大流算法和二分枚举方法解决一个有趣的优化问题:在有限的挤奶机和每个挤奶机最大处理牛的数量限制下,如何安排每头牛到挤奶机的路径,以使行走最远的牛距离最小。通过建立超级源点和超级汇点的网络流模型,确保所有牛都能被挤奶,并通过二分枚举找到最优解。

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

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

 

题意:有K个挤奶机器和C头牛,每个挤奶机器可以给M头牛挤奶,给出一个(K+C)*(K+C)的矩阵,前K行表示第i个挤奶机到其他点的距离,后C行表示第i头牛到其他点的距离。问,在所有的牛都可以被挤奶的前提下,走得最远的那头牛所走的距离最小。(有点拗口,也就是说,满足所有牛都可以被挤奶的方案有很多。所以,不同方案下,这些牛中,走的最远的那头牛需要走的距离也可能不一样,找出最小的那种情况)

 

思路:我们先来考虑怎么使得每头牛都可以被挤奶,如果把牛看做流量的话,如果所有的牛都被挤奶了 ,那么最大流=牛的数量。所以,我们引入超级源点,超级汇点,将源点向牛连边(流量为1),然后将牛向机器连边(流量为1),最后将机器向汇点连边(流量为M)。这样就可以跑最大流来判断牛是否可以全部被挤奶了。然后我们可以通过二分枚举答案了,在建边的时候,如果距离大于mid那么就不进行连边,如果最大流大于C说明边还可以小,那么令r=mid-1.否则令l=mid+1;最后的mid就是答案了。

 

#include<iostream>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
const int inf=1e9+10;
int K,C,M,e;
int Map[250][250],dis[250],head[250];
void init()
{
    e=0;
    memset(head,-1,sizeof head);
}
struct EDGE
{
    int to,flow,next;
};
EDGE edge[200000];
void add(int u,int v,int f)//前向星建边
{
    edge[e].to=v,edge[e].flow=f;
    edge[e].next=head[u];
    head[u]=e++;
    edge[e].to=u,edge[e].flow=0;
    edge[e].next=head[v];
    head[v]=e++;
}
void floyd()//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++){
                Map[i][j]=min(Map[i][j],Map[i][k]+Map[k][j]);
            }
}
bool bfs(int s)//dinic给图打标记分层
{
    memset(dis,-1,sizeof dis);
    queue<int> que;
    dis[s]=0;
    que.push(s);
    while(!que.empty()){
        int u=que.front();
        que.pop();
        for(int i=head[u];i!=-1;i=edge[i].next){
            int v=edge[i].to,f=edge[i].flow;
            if(dis[v]<0&&f>0){
                dis[v]=dis[u]+1;
                que.push(v);
            }
        }
    }
    return dis[K+C+1]>0;
}
int dfs(int u,int Min)//按分层寻找曾广路径
{
    if(u==K+C+1) return Min;
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].to,f=edge[i].flow;
        if(f>0&&dis[v]==dis[u]+1){
            int a=dfs(v,min(Min,f));
            if(a>0){
                edge[i].flow-=a;
                edge[i^1].flow+=a;//反弧加流
                return a;
            }
        }
    }
    return 0;
}
int dinic()
{
    int ans=0,tmp;
    while(bfs(0)){
        while(tmp=dfs(0,inf)){
            ans+=tmp;
        }
    }
    return ans;
}
void solve()
{
    int l,r,mid,ans;
    l=0,r=inf;
    while(l<=r){//二分枚举
        mid=(l+r)>>1;
        init();
        for(int i=K+1;i<=K+C;i++)
            for(int j=1;j<=K;j++){
                if(Map[i][j]<=mid) add(i,j,1);
            }
        for(int i=1;i<=K+C;i++){
            if(i>=K+1) add(0,i,1);
            else if(i<=K) add(i,K+C+1,M);
        }
        if(dinic()==C){
            ans=mid;
            r=mid-1;
        }
        else l=mid+1;
    }
    cout<<ans<<endl;
}
int main()
{
    while(cin>>K>>C>>M){
        for(int i=1;i<=K+C;i++)
            for(int j=1;j<=K+C;j++){
                cin>>Map[i][j];
                if(Map[i][j]==0) Map[i][j]=inf;
            }
        floyd();
        solve();
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值