poj 2112 Optimal Milking 二分+最大流

<<训练指南>>P367页提到的公平分配问题。

二分答案ans

建模:

构造二分图,奶牛看成X集合,挤奶机看成Y集合。

1、X和源S连边,边容量为1.

2、满足dist(x,y)<=ans的 X和Y连边,边容量为1

3、Y和汇T连边,边容量为m.(不能超过挤奶机上限)

这样,网络中的流量都是由S——》X——》Y——》T点 ,只有当网络的总流量等于K时,才意味着每一个奶牛都选择了一个挤奶器,也就是一个可行解。

通过log(n)次的最大流既可以求出答案。

dinic实现。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 250
#define INF 0x3f3f3f3f
struct edge
{
	int to,c,next;
};
edge e[999999];
int que[MAXN*100];
int dis[MAXN];
int pre[MAXN];
int head[MAXN],head2[MAXN];
int mp[MAXN][MAXN];
int st,ed;
int maxflow;
int en;
int n,m,k,c;
void add(int a,int b,int c)
{
	e[en].to=b;
	e[en].c=c;
	e[en].next=head[a];
	head[a]=en++;
	e[en].to=a;
	e[en].c=0;
	e[en].next=head[b];
	head[b]=en++;
}
bool bfs()
{
	memset(dis,-1,sizeof(dis));
	que[0]=st,dis[st]=1;
	int t=1,f=0;
	while(f<t)
	{
		int j=que[f++];
		for(int k=head[j];k!=-1;k=e[k].next)
		{
			int i=e[k].to;
			if(dis[i]==-1&&e[k].c)
			{
				que[t++]=i;
				dis[i]=dis[j]+1;
				if(i==ed) return true;

			}
		}
	}
	return false;
}
int update()
{
	int p,flow=INF;
    for (int i=pre[ed];i!=-1;i=pre[i])
		if(e[head2[i]].c<flow) p=i,flow=e[head2[i]].c;
    for (int i=pre[ed];i!=-1;i=pre[i])
		e[head2[i]].c-=flow,e[head2[i]^1].c+=flow;
    maxflow+=flow;
    return p;
}
void dfs()
{
	memset(pre,-1,sizeof(pre));
	memcpy(head2,head,sizeof(head2));
    for(int i=st,j;i!=-1;)
    {
        int flag=false;
        for(int k=head[i];k!=-1;k=e[k].next)
          if(e[k].c&&(dis[j=e[k].to]==dis[i]+1))
          {
                pre[j]=i;
				head2[i]=k;
				i=j;
				flag=true;
                if(i==ed)
					i=update();
                if(flag)
					break;
          }
        if (!flag) dis[i]=-1,i=pre[i];
    }
}
int dinic()
{
	maxflow=0;
	while(bfs())
		dfs();
	return maxflow;
}
void init()
{
	en=0;
	st=k+c+1;     //源
    ed=k+c+2;     //汇
	memset(head,-1,sizeof(head));
}
int l,r,mid;
void input()
{
    for(int i=1;i<=k+c;i++)
    {
        for(int j=1;j<=k+c;j++)
        {
            scanf("%d",&mp[i][j]);
            if(mp[i][j]==0) mp[i][j]=INF;
        }
    }
    int n=k+c;
    for(int k=1;k<=n;k++)
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                if(mp[i][j]>mp[i][k]+mp[k][j])
                    mp[i][j]=mp[i][k]+mp[k][j];
            }
        }
    }
}
void build(int mid)
{
    init();
    for(int i=1;i<=k;i++) add(i,ed,m);
    for(int j=k+1;j<=k+c;j++) add(st,j,1);
    for(int i=k+1;i<=k+c;i++)
    {
        for(int j=1;j<=k;j++)
        {
            if(mp[i][j]<=mid)
            {
                add(i,j,1);
            }
        }
    }
}
int cal()
{
    l=0;r=20000;
    int ans=0;
    while(l<=r)
    {
        mid=(l+r)>>1;
        build(mid);
        if(dinic()==c) {ans=mid;r=mid-1;}
        else l=mid+1;
    }
    return ans;
}
int main()
{
    while(scanf("%d%d%d",&k,&c,&m)!=EOF)
    {
        input();
        printf("%d\n",cal());
    }
}


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TommyTT

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值