(POJ 3281)Dining --最大流,匹配建图,Dinic

Dining
Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 10755 Accepted: 4930

Description
Cows are such finicky eaters. Each cow has a preference for certain foods and drinks, and she will consume no others.
Farmer John has cooked fabulous meals for his cows, but he forgot to check his menu against their preferences. Although he might not be able to stuff everybody, he wants to give a complete meal of both food and drink to as many cows as possible.
Farmer John has cooked F (1 ≤ F ≤ 100) types of foods and prepared D (1 ≤ D ≤ 100) types of drinks. Each of his N (1 ≤ N ≤ 100) cows has decided whether she is willing to eat a particular food or drink a particular drink. Farmer John must assign a food type and a drink type to each cow to maximize the number of cows who get both.
Each dish or drink can only be consumed by one cow (i.e., once food type 2 is assigned to a cow, no other cow can be assigned food type 2).
Input
Line 1: Three space-separated integers: N, F, and D
Lines 2..N+1: Each line i starts with a two integers Fi and Di, the number of dishes that cow i likes and the number of drinks that cow i likes. The next Fi integers denote the dishes that cow i will eat, and the Di integers following that denote the drinks that cow i will drink.
Output
Line 1: A single integer that is the maximum number of cows that can be fed both food and drink that conform to their wishes
Sample Input
4 3 3
2 2 1 2 3 1
2 2 2 3 1 2
2 2 1 3 1 2
2 1 1 3 3
Sample Output
3
Hint
One way to satisfy three cows is:
Cow 1: no meal
Cow 2: Food #2, Drink #2
Cow 3: Food #1, Drink #1
Cow 4: Food #3, Drink #3
The pigeon-hole principle tells us we can do no better since there are only three kinds of food or drink. Other test data sets are more challenging, of course.

题意:
有F种食物D种饮料,有n只牛他们有他们自己喜欢的食物和饮料,且每种食物和饮料只能给一只牛,问最多可以满足只牛,同时满足食物和饮料。

分析:
这题是个匹配问题。所以首先我们将牛,食物,饮料和他们之间可以匹配的关系表示出来。以第一只牛为例:(所以边的容量为1)
这里写图片描述
所以对于第一只牛总共有四种方案满足,但是最大的匹配数为2.这是因为我们给第一只牛两份食物和饮料。题目问的是牛满足的数目所以我们还要进一步修改。如下图:
这里写图片描述

我们在每只牛之间在建一条边,保证每只牛只能分配到一份食物和饮料。最后我们只需要再建立一个超级源点和汇点(源点和每种食物之间建边,每种饮料和汇点之间建边)。然后求出源点到汇点的最大流即可。

我的建边方式:0为源点,1~F为食物,F+1~F+2*n为牛,F+2*n+1~F+2*n+D为饮料,F+2*n+D+1为汇点。

AC代码:

#include<iostream>
#include<algorithm>
#include<string>
#include<sstream>
#include<set>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#include<deque>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<functional>
using namespace std;

#define N 600
#define INF 100000000
struct Edge
{
    int from,to,cap,flow;
    Edge(int u,int v,int c,int f):from(u),to(v),cap(c),flow(f){}
};
struct Dinic
{
    int n,m,s,t;//结点数,边数(包括反向弧),源点编号,汇点编号
    vector<Edge>edges;//边表,dges[e]和dges[e^1]互为反向弧
    vector<int>G[N];//邻接表,G[i][j]表示结点i的第j条边在e数组中的编号
    bool vis[N]; //BFS的使用
    int d[N]; //从起点到i的距离
    int cur[N]; //当前弧下标

    void addedge(int from,int to,int cap)
    {
        edges.push_back(Edge(from,to,cap,0));
        edges.push_back(Edge(to,from,0,0));
        int  m=edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
    }

    bool bfs()
    {
        memset(vis,0,sizeof(vis));
        queue<int>Q;
        Q.push(s);
        d[s]=0;
        vis[s]=1;
        while(!Q.empty())
        {
            int x=Q.front();Q.pop();
            for(int i=0;i<G[x].size();i++)
            {
                Edge&e=edges[G[x][i]];
                if(!vis[e.to]&&e.cap>e.flow)//只考虑残量网络中的弧
                {
                    vis[e.to]=1;
                    d[e.to]=d[x]+1;
                    Q.push(e.to);
                }
            }

        }
        return vis[t];
    }

    int dfs(int x,int a)//x表示当前结点,a表示目前为止的最小残量
    {
        if(x==t||a==0)return a;//a等于0时及时退出,此时相当于断路了
        int flow=0,f;
        for(int&i=cur[x];i<G[x].size();i++)//从上次考虑的弧开始,注意要使用引用,同时修改cur[x]
        {
            Edge&e=edges[G[x][i]];//e是一条边
            if(d[x]+1==d[e.to]&&(f=dfs(e.to,min(a,e.cap-e.flow)))>0)
            {
                e.flow+=f;
                edges[G[x][i]^1].flow-=f;
                flow+=f;
                a-=f;
                if(!a)break;//a等于0及时退出,当a!=0,说明当前节点还存在另一个曾广路分支。

            }
        }
        return flow;
    }

    int Maxflow(int s,int t)//主过程
    {
        this->s=s,this->t=t;
        int flow=0;
        while(bfs())//不停地用bfs构造分层网络,然后用dfs沿着阻塞流增广
        {
            memset(cur,0,sizeof(cur));
            flow+=dfs(s,INF);
        }
        return flow;
    }
};

int main()
{
  //freopen("in.txt","r",stdin);
  int n,F,D,fi,di,u,v;
  while(scanf("%d%d%d",&n,&F,&D)!=EOF)
  {
      Dinic dinic;
      for(int i=F+1;i<=F+n;i++)
      {
          scanf("%d%d",&fi,&di);
          for(int k=0;k<fi;k++)
          {
              scanf("%d",&u);
              dinic.addedge(u,i,1);
          }
          dinic.addedge(i,i+n,1);
          for(int k=0;k<di;k++)
          {
              scanf("%d",&v);
              dinic.addedge(n+i,F+n+n+v,1);
          }
      }
      for(int i=1;i<=F;i++)
        dinic.addedge(0,i,1);
      for(int i=F+n+n+1;i<=F+n+n+D;i++)
        dinic.addedge(i,F+n+n+D+1,1);
      printf("%d\n",dinic.Maxflow(0,F+n+n+D+1));
  }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值