poj 2112 Optimal Milking(最大流+二分 或者 二分图多重匹配)

题目链接:http://poj.org/problem?id=2112

 

题意:有n个挤奶机,m头奶牛,每个挤奶机最多处理k头奶牛。给出挤奶机、奶牛之间的距离,问完成挤奶任务后,使走最远距离奶牛最小化,并输出。

 

思路:最远距离最小化,很容易知道时二分处理。但是二分判定条件是什么呢?

二分距离成立的情况是 使所以奶牛都可以到挤奶机。这样变成了一个二分图匹配的问题,左边是n个挤奶机,  右边是m头奶牛,每个奶牛都要匹配一个挤奶机。又因为挤奶机有匹配个数的,就是二分图多重匹配了。只要最大匹配数量是奶牛的头数m则这个二分距离是成立的。(二分图多重匹配)

 

当然,用网络流最大流也是可以完成的,二分图一般都可以用网络流。那怎么建图呢?

只要建两个虚点,一个起点,一个终点,起点连接每个挤奶机,流量是k,每头奶牛连接终点,流量为1,而根据二分距离判定挤奶机是否可以连接奶牛,可以则流量是1。只要最大流是m,则说明二分距离是可以的。

当然这里的点与点之间的距离用floyed计算即可.

 

二分图多重匹配代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<queue>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn=350;
int mp[maxn][maxn];
bool used[maxn];
int cnt[maxn];//cnt[i]记录现在第i个挤奶机有多少个奶牛
int mat[maxn][maxn],n,m,k;//mat[i][j]表示第i个挤奶机第j个匹配的奶牛是谁 
int g[maxn][maxn];

bool dfs(int x)//匹配 
{
    for(int i=1;i<=n;i++)
    {
        if(mp[x][i]&&!used[i])//表示第x个奶牛可以匹配第i个挤奶机 
        {
            used[i]=1;
            if(cnt[i]<k)//并且该挤奶机数量未上限,直接匹配 
            {
                mat[i][cnt[i]++]=x;
                return true;
            }
            else//人数上限,查找该挤奶机上所以奶牛是否可以让位置 
            {
                for(int j=0;j<k;j++)
                {
                    if(dfs(mat[i][j]))//可以让位置 
                    {
                        mat[i][j]=x;
                        return true;
                    }
                }
            }
        }
    }
    return false;
}

bool solve() 
{
    for(int i=n+1;i<=n+m;i++)//查看是否所以奶牛可以匹配 
    {
        for(int j=1;j<=n;j++)
            used[j]=0;
        if(!dfs(i))//i奶牛不能匹配 
            return false;
    }
    return true;
}

bool check(int x)//判断条件,距离为x是否成立 
{
    memset(cnt,0,sizeof(cnt));
    memset(mp,0,sizeof(mp));
    for(int i=1;i<=n;i++)
        for(int j=n+1;j<=m+n;j++)
            if(g[i][j]<=x)
                mp[i][j]=mp[j][i]=1;
    if(solve())
        return true;
    return false;
}

int main()
{
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=n+m;i++)
    {
        for(int j=1;j<=n+m;j++)
        {
            scanf("%d",&g[i][j]);
            if(i!=j&&!g[i][j])
                g[i][j]=inf;
        }
    }
    for(int k=1;k<=n+m;k++)//floyed求最短距离 
        for(int i=1;i<=n+m;i++)
            for(int j=1;j<=n+m;j++)
                g[i][j]=min(g[i][j],g[i][k]+g[k][j]);    
                        
    int l=0,r=inf,mid;
    while(r>=l)//二分结果 
    {
        mid=(r+l)/2;
        if(check(mid))
            r=mid-1;    
        else
            l=mid+1;
    }
    printf("%d\n",l);
    return 0;
}
View Code

 

网络流代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<queue>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn=350;
int g[maxn][maxn];
struct node{
    int u,v,w,nxt;
}e[maxn*maxn];
int h[maxn],depth[maxn],n,m,k,st,ed;
int cnt;

void add(int u,int v,int w)//建图,记得建反向边 
{
    e[cnt].v=v;e[cnt].w=w;
    e[cnt].nxt=h[u];h[u]=cnt++;
    
    e[cnt].v=u,e[cnt].w=0;
    e[cnt].nxt=h[v];h[v]=cnt++;
}

bool bfs(){//dinic--分层图 
    queue<int> que;
    memset(depth,0,sizeof(depth));
    que.push(st);
    depth[st]=1;
    while(!que.empty()){
        int u=que.front();
        que.pop();
        if(u==ed)
            return true;
        for(int i=h[u];i!=-1;i=e[i].nxt){
            int v=e[i].v;
            int w=e[i].w;
            if(!depth[v]&&w){
                depth[v]=depth[u]+1;
                que.push(v);
            }
        }
    }
    return false;
}

int dfs(int u,int dis) 
{
    if(u==ed)
        return dis;
    int res=0;
    for(int i=h[u];i!=-1;i=e[i].nxt)
    {
        int v=e[i].v;
        int w=e[i].w;
        if((depth[v]==depth[u]+1)&&w)
        {
            int di=dfs(v,min(w,dis-res));
            e[i].w-=di;
            e[i^1].w+=di;
            res+=di;
            if(res==dis)
                return dis;
        }
    }
    return res;
}

int dinic()//dinic求最大流 
{
    int ans=0;
    while(bfs())
    {
        ans+=dfs(st,inf);
    }
    return ans;
}

bool check(int x)
{
    cnt=0;
    memset(h,-1,sizeof(h));
    for(int i=1;i<=n;i++)//挤奶机和奶牛是否可以连接 
        for(int j=n+1;j<=n+m;j++)
            if(g[i][j]<=x)
                add(i,j,1);
    for(int i=1;i<=n;i++)//起点连挤奶机 
        add(st,i,k);
    for(int i=n+1;i<=m+n;i++)//奶牛连终点 
        add(i,ed,1);
    int ans=dinic();
    if(ans==m)//最大流是奶牛头数 
        return true;
    return false;
}

int main()
{
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=n+m;i++)
    {
        for(int j=1;j<=n+m;j++)
        {
            scanf("%d",&g[i][j]);
            if(i!=j&&!g[i][j])
                g[i][j]=inf;
        }
    }
    for(int k=1;k<=n+m;k++)
        for(int i=1;i<=n+m;i++)
            for(int j=1;j<=n+m;j++)
                g[i][j]=min(g[i][j],g[i][k]+g[k][j]);            
    st=0,ed=n+m+1;//建起点,终点 
    int l=0,r=inf,mid;
    while(r>=l)
    {
        mid=(r+l)/2;
        if(check(mid))
            r=mid-1;    
        else
            l=mid+1;
    }
    printf("%d\n",l);
    return 0;
}
View Code

 

posted @ 2019-08-04 17:16  怀揣少年梦.#  阅读(357)  评论(0编辑  收藏  举报