poj 3281

题意:给出牛,饮料还有食物的数量,每头牛给出喜欢的饮料和食物,最后求出能够满足的牛的数量

一开始觉得是二分图匹配,但是怎么配两边的呢?
遂放弃。

然后 听说网络流可以弄二分图?

很好。怎么解决 一头牛对应一种呢?把牛拆成;两个点,中间连一条边权为1的边。

搞定。那现在说一下EK算法。

戳这里就好
给个模板题 1273

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>

using namespace std;

const int VM=220;
const int INF=0x3f3f3f3f;

int n,m,max_flow;   //max_flow是最大流
int map[VM][VM],flow[VM][VM];   // map[i][j]是每条边的容量,flow[i][j]是每条边的流量 
int res[VM],pre[VM];    //res[]是每个点的剩余流量,pre[]是每个点的父亲

int EK(int src,int des){
    max_flow=0;
    queue<int> q;
    while(!q.empty())
        q.pop();
    memset(flow,0,sizeof(flow));    //最开始每条边的流量都是0
    while(1){
        memset(res,0,sizeof(res));  //残余流量得变0,一开始所有点都没流入对吧
        res[src]=INF;   //源点嘛,剩余流量无限是必须的...
        q.push(src);    //从源点开始进行BFS找增广路
        int u,v;
        while(!q.empty()){
            u=q.front();
            q.pop();
            for(v=1;v<=m;v++)       //遍历所有点,找可行边
                if(!res[v] && map[u][v]>flow[u][v]){    //该点剩余流量为0 且 容量大于流量,也就是找到了新的结点 
                    pre[v]=u;   //找到新结点,父节点得记录一下吧
                    q.push(v);
                    res[v]=min(res[u],map[u][v]-flow[u][v]);    //如果u的剩余流量能填满uv就填满,不能的话就把u这点的流量全部流向uv 
                }
        }
        if(res[des]==0)     //如果当前已经是最大流,汇点没有残余流量
            return max_flow;
        for(u=des;u!=src;u=pre[u]){     //如果还能增广,那么回溯,从汇点往回更新每条走过的边的流量
            flow[pre[u]][u]+=res[des];  //更新正向流量   (注意这里更新的是流量,而不是容量)
            flow[u][pre[u]]-=res[des];  //更新反向流量   
        }
        max_flow+=res[des];
    }
}

int main(){
    while(~scanf("%d%d",&n,&m)){
        memset(map,0,sizeof(map));
        memset(pre,0,sizeof(pre));
        int u,v,w;
        while(n--){
            scanf("%d%d%d",&u,&v,&w);
            map[u][v]+=w;   //有重边
        }
        printf("%d\n",EK(1,m));
    }
    return 0;
}

所以这道题为什么WA了一次

因为 我没有建后向边,也就是凡是有一条正向的这样的边都要建一条反向的边权为0的边来退流!!!!

推流!!!不然时挂的!!
1.对算法本身并没有吃透
2.建边的技巧

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#define inf 0x3fffffff
//by mars_ch
using namespace std;
int n,f,d; 
struct data
{
    int f,t,nxt,c;
}e[100005];
int tot,first[605];
int flow[605][605];
int pre[605],res[605];
void add(int a,int b,int c)
{
    e[tot].f=a;
    e[tot].t=b;
    e[tot].c=c;
    e[tot].nxt=first[a];
    first[a]=tot++;
}
int EK(int s,int end)
{
    queue<int> q;
    int max_flow=0;

    while(1)
    {
        memset(res,0,sizeof(res));

        res[s]=inf;
        q.push(s);

        while(!q.empty())
        {
            int t=q.front();
            q.pop();
            for(int j=first[t];j!=-1;j=e[j].nxt)
            {
                int v=e[j].t;
                if(!res[v] && e[j].c>flow[t][v])
                {
                    pre[v]=t;
                    q.push(v);

                    res[v]=min(res[t],e[j].c-flow[t][v]);
                }
            }
        }
        if(res[end] == 0)
        {
            return max_flow;
        }
        for(int i=end;i!=s;i=pre[i])
        {
            flow[pre[i]][i]+=res[end];
            flow[i][pre[i]]-=res[end];
        }
        max_flow+=res[end];
    }
}
int main()
{
    memset(first,-1,sizeof(first));
    scanf("%d%d%d",&n,&f,&d);
    int s=f+2*n+d+1;
    for(int i=1;i<=f;i++)
    {
        add(0,i,1);
        add(i,0,0);
    }
    for(int i=f+1;i<=f+n;i++)
    {
        add(i,i+n,1);
        add(i+n,i,0);
    }
    for(int i=f+2*n+1;i<=f+2*n+d;i++)
    {
        add(i,s,1);
        add(s,i,0);
    }
    for(int i=1;i<=n;i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        for(int j=1;j<=a;j++)
        {
            int x;
            scanf("%d",&x);
            add(x,i+f,1);
            add(i+f,x,0); 
        }
        for(int j=1;j<=b;j++)
        {
            int x;
            scanf("%d",&x);
            add(i+n+f,x+f+2*n,1);
            add(x+f+2*n,i+n+f,0); 
        } 
    }
    int ans=EK(0,s);
    printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值