POJ-3281 Dining(最大流)

  题目:POJ-3281 Dining

  题目大意:一个农场场主,为他的n头牛做了f种食物和d种饮料,每头牛都有他喜欢的食物和饮料,而且,每个食物或饮料都只能给一头牛食用,问最多能同时满足多少头牛的饮食。

  解题思路:这个题目我只记得我一开始看网络流的时候看到过,当时这个构图并不会,但现在看起来好像就没当初那么难了。这个题目如果只有食物和牛的话就有点二分图的意思了,可是加了喜欢的饮料,所以需要网络流做。难点当然在构图,看到这个题目的第一感觉是从源点到牛有边权值为2表示一头牛需要两样东西满足,牛到喜欢的饮料和食物分别有边权值为1,然后食物饮料到汇点有边权值为1表示只能有一个牛使用,然后看看最大流的流值的一半就是牛的个数,但是明显不对,因为2个流值可能不同属于一头牛,但是最直观的想法往往可以反应问题,不过需要更加苛刻的限制,这样就想到了一牛一流,也就是食物连牛,权值为1,牛连饮料,权值为1,食物饮料分别连接原点和汇点,权值都是1。构图完成,流的最大值就是牛的个数。

  AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
#include<vector>
#define maxn 510
#define INF 1e9
using namespace std;
struct edge{
    edge(){};
    edge(int from,int to,int val,int flow):from(from),to(to),val(val),flow(flow){}
    int from,to,val,flow;
};
int n,m,f,d;
vector<edge>p;
vector<int>num[maxn];
bool visit[maxn];
int dis[maxn];
int cur[maxn];
void add(int x,int y,int z){
    p.push_back(edge(x,y,z,0));
    p.push_back(edge(y,x,0,0));
    int xx=p.size();
    num[x].push_back(xx-2);
    num[y].push_back(xx-1);
}
bool bfs(int s,int t){
    int i,j;
    memset (visit,0,sizeof(visit));
    queue<int>qq;
    qq.push(s);
    dis[s]=0;
    visit[s]=1;
    while (!qq.empty()){
        int u=qq.front();
        qq.pop();
        for (i=0;i<num[u].size();i++){
            edge& e=p[num[u][i]];
            if (!visit[e.to]&&e.val>e.flow){
                visit[e.to]=1;
                dis[e.to]=dis[u]+1;
                qq.push(e.to);
            }
        }
    }
    return visit[t];
}
int dfs(int x,int t,int val){
    if (x==t||val==0){
        return val;
    }
    int flow=0,f;
    for (int& i=cur[x];i<num[x].size();i++){
        edge& e=p[num[x][i]];
        if (dis[x]+1==dis[e.to]&&(f=dfs(e.to,t,min(val,e.val-e.flow)))>0){
            e.flow+=f;
            p[num[x][i]^1].flow-=f;
            flow+=f;
            val-=f;
            if (val==0)break;
        }
    }
    return flow;
}
int dinic(int s,int t){
    int flow=0;
    while (bfs(s,t)){
        memset (cur,0,sizeof(cur));
        flow+=dfs(s,t,INF);
    }
    return flow;
}
int main(){
    int i,j,k,l,x,y,z,t;
    while (scanf("%d%d%d",&n,&f,&d)!=EOF){
        int total=2*n+d+f+1;
        for (i=1;i<=f;i++)add(0,i,1);
        for (i=1;i<=d;i++)add(i+2*n+f,total,1);
        for (i=1+f;i<=n+f;i++){
            add(i,n+i,1);
            scanf("%d%d",&x,&y);
            while (x--){
                scanf("%d",&z);
                add(z,i,1);
            }
            while (y--){
                scanf("%d",&z);
                add(n+i,z+2*n+f,1);
            }
        }
        printf("%d\n",dinic(0,total));
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值