POJ 2112.Optimal Milking【Floyd+二分+最大流】

意:有K台挤奶机器和C头牛(统称为物体),每台挤奶机器只能容纳M头牛进行挤奶。

现在给出dis[K + C][K + C]的矩阵,dis[i][j]若不为0则表示第i个物体到第j个物体之间有路,dis[i][j]就是该路的长度。(1 <= K <= 30,1 <= C <= 200)
现在问怎么安排这C头牛到K台机器挤奶,使得需要走最长路程到挤奶机器的奶牛所走的路程最少,求出这个最小值。

Sample Input


2 3 2    // K C M
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


利用Floyd算法求出每个奶牛到每个挤奶机的最短距离。
则题目变为:
已知C头奶牛到K个挤奶机的距离,每个挤奶机只能有M个奶牛,每个奶牛只能去一台挤奶机,求这些奶牛到其要去的挤奶机距离的最大值的最小值。

模型

每个奶牛和挤奶器都是一个节点,添加一个源,连边到所有奶牛节点,这些边容量都是1。
添加一个汇点,每个挤奶器都连边到它。这些边的容量都是M
先假定一个最大距离的的最小值 maxdist, 在上述图中,如果奶牛节点i和挤奶器节点j之间的距离<= maxdist,则从i节点连一条边到j节点,表示奶牛i可以到挤奶器j去挤奶。该边容量为1。该图上的最大流如果是C(奶牛数),那么就说明假设的 maxdist成立,则减小 maxdist再试,如果最大流小于C,则增大maxdis【每次二分都要重新建图】


#include <string.h>
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
#define REP( i , n ) for ( int i = 0 ; i < n ; ++ i )
#define FOR( i , a , b ) for ( int i = a ; i <= b ; ++ i )
#define CLR( a , x ) memset ( a , x , sizeof (a) );
#define RE freopen("1.in","r",stdin);
#define WE freopen("output.txt","w",stdout);
#define debug(x) cout<<#x<<":"<<(x)<<endl;
const int inf=0x3f3f3f3f;
const int maxn=520;//!!!是500以上,不是200
int tab[maxn][maxn],mat[maxn][maxn],dis[maxn];  //tab为流量,dis为层次
//bfs找层次图
int k,c,m,des,src;
int bfs(int s,int t)
{
    int q[maxn],head=0,tail=0;
    q[tail++]=s;
    memset(dis,-1,sizeof(dis));
    dis[s]=0;
    while(head<tail)
    {
        int cur=q[head++];
        for(int i=src;i<=des;i++)
        {
            if(dis[i]<0&&tab[cur][i]>0)
            {
                dis[i]=dis[cur]+1;
                q[tail++]=i;
            }
        }
    }
    if(dis[t]>0)    return 1;
    return 0;   //dis[t]=-1:路不通
}

int dfs(int s,int t,int low)//Low为增广路径上的最小流量
{
    int flow=0;
    if(s==des)    return low; //到汇点直接返回目前为止的最小流量
    for(int i=src;i<=des;i++)
    {       //在下一层里找
        if(tab[s][i]>0
           &&dis[i]==dis[s]+1
           &&(flow=dfs(i,t,min(low,tab[s][i]))))
        {
            tab[s][i]-=flow;    //不断的减流量
            tab[i][s]+=flow;
            return flow;        //能到汇点
        }
    }
    return 0;
}
void floyd()
{
    int num=k+c;
    FOR(l,1,num)
        FOR(i,1,num)
            FOR(j,1,num)
                mat[i][j]=min(mat[i][j],mat[i][l]+mat[l][j]);
}
int main()
{
    #ifndef ONLINE_JUDGE
        RE
    #endif // ONLINE_JUDGE

    while(scanf("%d%d%d",&k,&c,&m)!=EOF)
    {
        CLR(mat,0);
        src = 0,des = k + c + 1;
        FOR(i,1,k+c)
            FOR(j,1,k+c){
                scanf("%d",&mat[i][j]);
                if(i!=j&&mat[i][j]==0)
                    mat[i][j]=inf;
            }

        floyd();
        int low=0,high=200*200;

        int assAns;
        while(low<high)
        {
            assAns=(low+high)/2;

            CLR(tab,0);
            FOR(i,k+1,k+c)
                tab[src][i]=1;
            FOR(i,1,k)
                tab[i][des]=m;

            FOR(i,k+1,k+c)
                FOR(j,1,k)
                    if(mat[i][j]<=assAns)
                        tab[i][j]=1;

            int ans=0,tans=0;
            while(bfs(src,des))         //直到源点不能到汇点为止
                while(tans=dfs(src,des,inf))     //在同一个层次图里尽量找增广路
                    ans+=tans;

            if(ans<c)
                low=assAns+1;
            else if(ans>=c)
                high=assAns;
        }
        printf("%d\n",low);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值