网络流 模板【Usaco2007 Open】bzoj1711 Dining吃饭

网络流
网络流通常有一个源点,一个汇点,和其他若干点,这些点之间有若干条有向边,每条边上有一个负载上限,称为容量。题目通常要求我们求出在所有边容量允许的范围内,从源点到汇点流量的最大值。
一般情况下,网络流都不会很难写,网络流题目大多数都难在建图上。

dinic算法:
step1:
bfs给所有有可行流的点标号,建立层次网络,源点标为1,将所有能标的点标完或标到汇点结束;
step2:
如果汇点被标记,dfs找到所有可行流并修改路径上的容量;
如果汇点没有被标记,结束算法。

注意事项:
1、每次bfs时每次标记数组要清空;
2、bfs在拓展状态时不仅要看是否被标记过,还要看是否有可行流;
3、bfs可以全部标记,也可以标记到汇点就结束;
4、dfs回溯时,反向边要加上流量;
5、dfs结束时,可以判断是否有通过该点的流量,如果没有,可以把该点从层次网络中清除,进行优化。

模板代码如下:

bool bfs()
{
    static int dl[N];
    memset(d,0,sizeof(d));
    int l=1,r=1;
    dl[1]=s;d[s]=1;
    while(l<=r)
    {
        int c=dl[l++];
        for(int t=fir[c];t;t=nes[t])
        {
            if(d[v[t]] || !q[t]) continue;
            dl[++r]=v[t];
            d[v[t]]=d[c]+1;
            if(v[t]==e) return true;
        }
    }
    return false;
}
int dfs(int c,int flow)
{
    if(c==e || flow==0) return flow;
    int ans=0;
    for(int t=fir[c];t;t=nes[t])
    {
        if(d[v[t]]!=d[c]+1 || !q[t]) continue;
        int f=dfs(v[t],min(flow,q[t]));
        ans+=f;flow-=f;
        q[t]-=f; q[t^1]+=f;
        if(flow==0) break;
    }
    if(!ans) d[c]=-1;
    return ans;
}
int dinic()
{
    int ans=0;
    while(bfs()) ans+=dfs(s,lar);
    return ans;
}

【Usaco2007 Open】bzoj1711 Dining吃饭
题目大意:
有n头牛,F种食物,G种饮料
每头牛分别喜欢一些食物和一些饮料
每种饮料和食物都只有一个
问最多能让几头牛吃到自己喜欢的食物并喝到自己喜欢的饮料

题目分析:
从源点向每种食物连一条容量为1的边,代表每种食物只有一个;
从每种饮料向汇点连一条容量为1的边,代表每种饮料只有一个;
然后用两个点代表一头牛,中间连一条容量为1的边,代表每头牛只选择一套食物和饮料;
把第一个代表牛的点和喜欢的食物连一条容量为1的边,把第二个代表牛的点和喜欢的饮料连一条容量为1的边;
然后跑最大流就可以了。

注意事项:
1、在写邻接表存边的时候要写接口(不写接口会死人的呀QAQ,多么痛的领悟);
2、在给所有的点编号的时候要小心不要把两个点的编号编重;
3、背好模板。
主函数代码如下:

void edge(int x,int y,int z)
{
    top++;
    v[top]=y;q[top]=z;
    nes[top]=fir[x];fir[x]=top;
}
int main()
{
    scanf("%d%d%d",&n,&F,&D);
    for(int i=1;i<=n;i++) edge(i,i+100,1,++top),edge(i+100,i,0,++top);
    for(int i=1;i<=F;i++) edge(s,200+i,1,++top),edge(200+i,s,0,++top);
    for(int i=1;i<=D;i++) edge(300+i,e,1,++top),edge(e,300+i,0,++top);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&fi,&di);
        for(int j=1;j<=fi;j++)
        {
            scanf("%d",&x);
            edge(200+x,i,1,++top);
            edge(i,200+x,0,++top);
        }
        for(int j=1;j<=di;j++)
        {
            scanf("%d",&x);
            edge(i+100,300+x,1,++top);
            edge(300+x,i+100,0,++top);
        }
    }
    printf("%d",dinic());
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值