poj2112解题报告(最大流 EK算法)

题目大意:

k个机器,每个机器最多服务m头牛。

c头牛,每个牛需要1台机器来服务。

告诉你牛与机器每个之间的直接距离。

问:让所有的牛都被服务的情况下,使走的最远的牛的距离最短,求这个距离。

解题思路:

Floyd求出各个点之间最短距离,二分枚举最短距离(上界是最大的最短距离,下界是最小的最短距离),Ek算法建图的注意最小距离大于mid的距离不给容量。

注意点:邻接举证为0的点是无限大.注意这个地方,有些时候需要判断一发

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int maxn=250;
const int INF=99999999;
int n,m,sum,s1,t1;//s,t为始点和终点
int flow[maxn][maxn],cap[maxn][maxn],a[maxn],p[maxn],d[maxn][maxn],k,c,milks;
//分别为:flow[u][v]为<u,v>流量、cap[u][v]为<u,v>容量、a[i]表示源点s到节点i的路径上的最小残留量、p[i]记录i的前驱
void Edmonds_Karp(int s,int t)
{
    int i,u,v;
    queue<int>q;//队列,用bfs找增广路
    while(1)
    {
        memset(a,0,sizeof(a));//每找一次,初始化一次
        a[s]=INF;
        q.push(s);//源点入队
        while(!q.empty())
        {
            u=q.front();
            q.pop();
            for(v=1; v<=m; v++)
            {
                if(!a[v]&&flow[u][v]<cap[u][v])
                {
                    p[v]=u;
                    q.push(v);
                    a[v]=min(a[u],cap[u][v]-flow[u][v]);//s-v路径上的最小残量
                }
            }
        }
        if(a[t]==0)//找不到增广路,则当前流已经是最大流
            break;
        sum+=a[t];//流加上
        for(i=t; i!=s; i=p[i]) // //从汇点顺着这条增广路往回走
        {
     //      cout<<i<<" ";
            flow[p[i]][i]+=a[t];//更新正向流量
            flow[i][p[i]]-=a[t];//更新反向流量
        }
       // cout<<endl;
    }
}
int main()
{
    int v,u,w;
    while(scanf("%d%d%d",&k,&c,&milks)!=EOF)//n是边数,m是点数
    {
        int up=-1;
        int down=INF;
        for(int i=1; i<=c+k; i++)
            for(int j=1; j<=c+k; j++)
                scanf("%d",&d[i][j]);
        for(int k1 = 1; k1 <= c+k; k1++)
            for(int i = 1; i <= c+k; i++)
                for(int j = 1; j <= c+k; j++)
                    if(d[i][k1]&&d[k1][j]&&(d[i][j]==0||d[i][j]>d[i][k1] + d[k1][j]))
                    {
                        d[i][j]=d[i][k1] + d[k1][j];
                      up=max(up,d[i][j]);
                      down=min(down,d[i][j]);
                    }
        m=c+k+2;
        t1=c+k+2;
        s1=c+k+1;
        int ans=up;
      //  down=0;up=ans;
        while(down<=up)
        {
            int mid=(down+up)/2;
            sum=0;//记录最大流量
            memset(flow,0,sizeof(flow));//初始化
            memset(cap,0,sizeof(cap));
            for(int i=1; i<=k; i++) //源点到挤奶机
                cap[s1][i]=milks;
            for(int i=k+1; i<=k+c; i++) //奶牛到汇点
                cap[i][t1]=1;
            for(int i = 1; i <= k; i++)//挤奶机到奶牛
                for(int j = k + 1; j <=k+ c; j++)
                    cap[i][j] = (d[i][j] != 0 && d[i][j] <= mid);
            Edmonds_Karp(s1,t1);
         //   cout<<down<<"  "<<up<<" "<<sum<<endl;
            if (sum == c)
            {
                if(mid < ans) ans = mid;
                up = mid - 1;
            }
            else down = mid + 1;
        }
        printf("%d\n",ans);
    }
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值