POJ 3281 Dining (最大流,建图拆点)

题目地址:http://poj.org/problem?id=3281

题目大意:目前有n只牛,f种食物,d种饮料,每种饮料和食物都只有1份,现在我们知道了每头牛喜欢的食物种类和饮料种类,求在为每头牛只分1份食物和一份饮料的情况下,最多能满足多少头牛的需求。

这题我们可以看出是网络流的最大流问题,不过难点还是出现在了建图上。。。

也许我们能想到,把牛列在食物和饮料之间,从s向食物连边,食物向牛连边,牛向饮料连边,饮料再向t连边,那么每次跑出来的路径就能是每头牛跟它喜欢的食物饮料的最大流了
在这里插入图片描述
但这真的就可以了嘛?并不行(一开始没想到这个,直接wa到裂开 qwq ),例如上面这个图,我们会发现我们能找到的最大流会是2(s-1-1-1-t)(s-2-1-2-t),显然这是不行的,因为这个答案是1,只有一头牛。

那我们怎么防止这种一头牛吃两组套餐的出现呢?这回就要涉及到拆点这种方法了,我们把每头牛拆成两个点,然后这两个点之间只连一条流量为1的边
在这里插入图片描述
这样建图之后,我们会发现每头牛最多也只能有1流量了(S-1-1-1-1-T),就可以避免上面的问题了

那么图建成功了,网络流就能直接上了!

直接上代码:

#include<queue> 
#include<cstring>
#include<iostream>
#define ll long long
#define endl '\n'
using namespace std;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
const int N=1e5+5;
struct qxx{
    int fr,w,y;
}q[N];
int n,m,s,t,ans,n1,n2,n3;
int head[1005],zs=-1;
int dis[1005];
void cun(int u,int v,int w)
{
    q[++zs].fr=head[u];
    q[zs].y=v; q[zs].w=w;
    head[u]=zs;
}
void build()
{
	for(int i=1;i<=n1;i++)   //s点连食物 
        cun(0,i,1),cun(i,0,0);
    for(int i=n1+n2*2+1;i<=n1+n2*2+n3;i++)   //饮料连t点 
        cun(i,t,1),cun(t,i,0);
    for(int i=1;i<=n2;i++)
	{
        cun(n1+i,n1+i+n2,1); cun(n1+i+n2,n1+i,0);   //拆点之后,两点连边 
        int num1,num2,x;
        cin>>num1>>num2;
        for(int j=1;j<=num1;j++)
		{
            cin>>x;
            cun(x,n1+i,1),cun(n1+i,x,0);   //食物连牛的第一个点 
        }
        for(int j=1;j<=num2;j++)
		{
            cin>>x;
            cun(n1+i+n2,n1+n2*2+x,1),cun(n1+n2*2+x,n1+i+n2,0);   //再用牛的第二个点去连饮料 
        }
    }
}
bool bfs()
{
    memset(dis,-1,sizeof(dis));
    queue<int> q1;
    q1.push(s); dis[s]=0;
    while(!q1.empty())
	{
        int u=q1.front(); q1.pop();
        for(int i=head[u];i!=-1;i=q[i].fr)
		{
            int v=q[i].y;
            if(dis[v]==-1&&q[i].w)
			{
                q1.push(v);
                dis[v]=dis[u]+1;
            }
        }
    }
    return dis[t]!=-1;
}
int dfs(int u,int f)
{
    if(u==t) return f;
    int res=f;
    for(int i=head[u];i!=-1;i=q[i].fr)
	{
        int v=q[i].y;
        if(dis[v]>dis[u]&&q[i].w>0)
		{
			int d=dfs(v,min(f,q[i].w));
            q[i].w-=d; q[i^1].w+=d;
            res-=d;
            if(!res) break;
        }
    }
    if(!res) dis[u]=-1;
    return f-res;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cin>>n2>>n1>>n3;
    s=0; t=n1+n2*2+n3+1; 
    memset(head,-1,sizeof(head));
    build();   //建图 
    while(bfs())   //跑最大流 
        ans+=dfs(s,1e9);
    cout<<ans;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值