HLG 1392 (POJ 2112 Optimal Milking)【二分+二分图多重匹配 + 最大流】

本文探讨了一个涉及机器与牛分配的优化问题,通过Floyd算法计算最短路径,采用二分图多重匹配解决分配问题。同时提供最大流算法作为备选解决方案,以确保所有牛都能被有效分配到机器上,且走的距离最小。

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

题意: 给你 n 个机器,m 头牛,每台机器最多可供 cap 头牛使用,并告诉你每个物体之间的距离,找出一个距离满足所有的牛都分配到机器且里面走最远的牛

    走的距离最小。

分析: 用 floyd 计算出每个物体的最短距离,然后二分枚举距离,如果在该距离下满足完全匹配,该距离即为合法距离,匹配用到多重匹配。

 二分图多重匹配
#include<stdio.h>
#include<string.h>
#define clr(x)memset(x,0,sizeof(x))
int cap;
int link[33][202];
int vlink[33];
int v[33];
struct node
{
    int to,next;
}q[200000];
int head[202];
int g[240][240];
int tot;
int n,m;
void add(int s,int u)
{
    q[tot].to=u;
    q[tot].next=head[s];
    head[s]=tot++;
}
int find(int x)
{
    int i,j,k;
    for(i=head[x];i;i=q[i].next)
    {
        k=q[i].to;
        if(!v[k])
        {
            v[k]=1;
            if(vlink[k]<cap)
            {
                link[k][vlink[k]++]=x;
                return 1;
            }
            for(j=0;j<vlink[k];j++)
            {
                if(find(link[k][j]))
                {
                    link[k][j]=x;
                    return 1;
                }
            }
        }
    }
    return 0;
}
int ok(int ti)
{
    int i,j,sum=0;
    tot=1;
    clr(head);
    clr(vlink);
    for(i=n+1;i<=n+m;i++)
        for(j=1;j<=n;j++)
            if(g[i][j]&&g[i][j]<=ti)
                add(i-n,j);
    for(i=1;i<=m;i++)
    {
        clr(v);
        if(find(i))
            sum++;
        else break;
    }
    if(sum==m)
        return 1;
    return 0;
}
int main()
{
    int i,j,k,low,high,mid;
    while(scanf("%d%d%d",&n,&m,&cap)!=EOF)
    {
        for(i=1;i<=n+m;i++)
            for(j=1;j<=n+m;j++)
                scanf("%d",&g[i][j]);
        for(k=1;k<=n+m;k++)
            for(i=1;i<=n+m;i++)
            {
                if(g[i][k])
                for(j=1;j<=n+m;j++)
                    if(g[k][j]&&(g[i][k]+g[k][j]<g[i][j]||g[i][j]==0))
                        g[i][j]=g[i][k]+g[k][j];
            }
        low=0;
        high=0;
        for(i=n+1;i<=n+m;i++)
            for(j=1;j<=n;j++)
                if(g[i][j]>high)
                    high=g[i][j];
        while(low<high)
        {
            mid=(low+high)>>1;
            if(ok(mid))
                high=mid;
            else low=mid+1;
        }
        printf("%d\n",low);
    }
    return 0;
}

 最大流做法:

#include<stdio.h>
#include<string.h>
#define INF 0x1f;
int n,m,s,u,e;
int cap;
int c[232][232];
int gap[232];
int dis[232];
int g[232][232];
void init()
{
    int v,x,q[232],front=0,rear=0;
    memset(gap,0,sizeof(gap));
    memset(dis,0xff,sizeof(dis));
    q[rear++]=u;
    dis[u]=0;
    while(front<rear)
    {
        x=q[front++];
        gap[dis[x]]++;
        for(v=0;v<=e;v++)
            if(dis[v]==-1&&c[v][x]>0)
            {
                dis[v]=dis[x]+1;
                q[rear++]=v;
            }
    }
}
int sap()
{
    init();
    int flow=0,top=s,i,j,k,pre[232],low[232];
    //memset(low,0,sizeof(low));
    while(dis[s]<=e)
    {
        int flag=0;
        low[s]=INF;
        for(i=0;i<=e;i++)
            if(c[top][i]>0&&dis[top]==dis[i]+1&&dis[i]>=0)
            {
                flag=1;
                break;
            }
        if(flag)
        {
            low[i]=c[top][i];
            if(low[i]>low[top])
                low[i]=low[top];
            pre[i]=top;
            top=i;
            if(top==u)
            {
                flow+=low[u];
                j=top;
                while(j!=s)
                {
                    k=pre[j];
                    c[k][j]-=low[u];
                    c[j][k]+=low[u];
                    j=k;
                }
                top=s;
                memset(low,0,sizeof(low));
            }
        }
        else 
        {
            int min=e;
            for(j=0;j<=e;j++)
                if(c[top][j]>0&&dis[j]+1<min&&dis[j]>=0)
                    min=dis[j]+1;
            gap[dis[top]]--;
            if(gap[dis[top]]==0)
                break;
            gap[min]++;
            dis[top]=min;
            if(top!=s)
                top=pre[top];
        }
    }
    return flow;
}
int ok(int ti)
{
    int i,j,sum;
    memset(c,0,sizeof(c));
    s=0;                           // 超级源点 s   0
    e=u=n+m+1;                     // 超级汇点 u   n+m+1
    for(i=n+1;i<=n+m;i++)          // 牛的编号为   n+1..n+m
        c[0][i]=1;                 // 机器的编号为 1..n
    for(i=n+1;i<=n+m;i++)              
        for(j=1;j<=n;j++)
            if(g[i][j]&&g[i][j]<=ti) // 如果牛和机器之间有边,且边距离小于 ti,
                c[i][j]=1;           // 容量设为 1
    for(i=1;i<=n;i++) 
        c[i][u]=cap;                 // 机器和汇点之间连边
    sum=sap();                       // 求最大流
    if(sum==m)                        // 最大流为 牛的数量
        return 1;
    return 0;
}
int main()
{
    int i,j,k,low,high,mid,t;
    while(scanf("%d%d%d",&n,&m,&cap)!=EOF)
    {
        for(i=1;i<=n+m;i++)
            for(j=1;j<=n+m;j++)
                scanf("%d",&g[i][j]);
        for(k=1;k<=n+m;k++)
            for(i=1;i<=n+m;i++)
            {
                if(g[i][k])
                for(j=1;j<=n+m;j++)
                    if(g[k][j]&&(g[i][k]+g[k][j]<g[i][j]||g[i][j]==0))
                        g[i][j]=g[i][k]+g[k][j];
            }
        low=0;
        high=0;
        for(i=n+1;i<=n+m;i++)
            for(j=1;j<=n;j++)
                if(g[i][j]>high)
                    high=g[i][j];
        while(low<high)        //  二分枚举答案
        {
            mid=(low+high)>>1;
            if(ok(mid))       //   枚举的答案可以保证 每头牛都分配到机器
                high=mid;
            else low=mid+1;
        }

        printf("%d\n",low);
    }
    return 0;
}

 

 

转载于:https://www.cnblogs.com/dream-wind/archive/2012/04/22/2464883.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值