poj 3281

       这是一道最大流的题,在构图上比上一道更难一些。

       如何构图可以参考http://blog.sina.com.cn/s/blog_6635898a0100ly5j.html

       做这道题时,用了Dinic算法。写了两个程序,一个是链式前向星存图(参考《ACM—ICPC程序设计系列 图论及应用》(哈尔滨工业大学出版社)P160),一个是基于STL的邻接表(参考《挑战程序设计竞赛》 人民邮电出版社 P216)。

       Dinic算法与之前的EK算法和Ford-Fulkerson算法相比效率更高,因为其先对图进行分层,然后多路增广,所以效率更高。基本思路是先有宽度优先搜索把图分层,然后根据分层的情况进行深度优先搜索,当无法增广时,再次分层,如果分层无法到达汇点,则结束(详细参考上面的两本书)。所谓多路增广,就是可以一次DFS就可以得到多条增广路,这一点在链式前向星版本体现的明显些。而在邻接表版本中,则通过当前弧对DFS进行优化,多路增广的特性似乎没有很好体现。

        通过在poj上多次提交发现,链式前向星版本快于邻接表版本,可能是因为这个邻接表基于STL比较慢,而且每次处理完一个测试用例要clear把图清空,也增加了时间开销。在上面说到的细节优化采取的策略上也有所影响。

        虽然是同一个算法,但是由于存图的方式不同,所以细节处理不同,其中的差异值得去学习。

        与上次同样,第一个可以用C++交(G++没试过),第二个必须用G++交。

代码(C++,链式前向星):

#include <cstdlib>
#include <iostream>
#include <queue>
#include <algorithm>

#define MAX 409
#define INF 2000000000
using namespace std;

//#define LOCAL

struct Edge{
    int to;
    int next;
    int cap;   
} edge[MAX*MAX*MAX*2];

int head[MAX],c,level[MAX];

void add_edge(int u,int v,int cap)
{
     edge[c].to=v;
     edge[c].next=head[u];
     edge[c].cap=cap;
     head[u]=c;
     c++;
     
     edge[c].to=u;
     edge[c].next=head[v];
     edge[c].cap=0;
     head[v]=c;
     c++;
}

int bfs(int s)
{
    int u,v,i;
    queue<int> qi;    
    memset(level,-1,sizeof(level));
    qi.push(s);
    level[s]=0;
    while(!qi.empty())
    {
         u=qi.front();
         qi.pop(); 
         for(i=head[u];i!=-1;i=edge[i].next)
         {
             v=edge[i].to;
             if(edge[i].cap>0&&level[v]==-1)
             {
                 level[v]=level[u]+1;
                 qi.push(v);                           
             }                    
         }             
    }
}

int dfs(int s,int t,int f)
{
    int i,v,d,res;
    if(s==t) return f;
    res=0;
    for(i=head[s];i!=-1;i=edge[i].next)
    {
         v=edge[i].to;                              
         if(level[s]+1==level[v]&&edge[i].cap>0)
         {
              d=dfs(v,t,min(edge[i].cap,f-res)); 
              if(d>0)
              {
                  edge[i].cap-=d;
                  edge[i^1].cap+=d;
                  res+=d;  
                  if(res==f) return res; 
              }                          
         }                               
    } 
    return res;
}

int dinic(int s,int t)
{
    int f,flow; 
    flow=0;     
    while(1)
    {
         bfs(s);
         if(level[t]==-1) break;
         f=dfs(s,t,INF);
         flow+=f;    
    }
    return flow; 
}

int main(int argc, char *argv[])
{
#ifdef LOCAL
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
#endif

    int n,f,d,fi,di,p,i,j;
    while(scanf("%d %d %d",&n,&f,&d)!=EOF)//超级源点为0,超级汇点为d+300+1 
    {                                     //食物编号:1~f  牛编号:1+100~n+100,1+200~n+200  饮料编号:1+300~d+300                                 
        c=0;            
        memset(head,-1,sizeof(head));  
        
        for(i=1;i<=f;i++)  add_edge(0,i,1); //添加超级源点到食物的边       
        for(i=1;i<=n;i++)
        {
            scanf("%d %d",&fi,&di);
            for(j=0;j<fi;j++)
            {
                scanf(" %d",&p);
                add_edge(p,i+100,1);  //添加食物到牛的边                           
            }
            add_edge(i+100,i+200,1); 
            for(j=0;j<di;j++)
            {
                scanf(" %d",&p);                                                                                           
                add_edge(i+200,p+300,1);  //添加牛到饮料的边                                              
            }                     
        }
        for(i=1;i<=d;i++) add_edge(i+300,d+300+1,1); //添加饮料到超级汇点的边 

        printf("%d\n",dinic(0,d+300+1));           
    } 
    system("PAUSE");
    return EXIT_SUCCESS;
}

代码(G++,邻接表):

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

#define MAX 409
#define INF 2000000000
using namespace std;

struct Edge{
    int to;
    int cap;
    int rev;
};
vector<Edge> G[MAX];

int level[MAX],iter[MAX];

void add_edge(int from,int to,int cap)
{
    G[from].push_back((Edge){to,cap,G[to].size()});
    G[to].push_back((Edge){from,0,G[from].size()-1});
}

void bfs(int s)
{
    int i,u;
    queue<int> qi;
    memset(level,-1,sizeof(level));
    qi.push(s);
    level[s]=0;
    while(!qi.empty())
    {
        u=qi.front();
        qi.pop();
        for(i=0;i<G[u].size();i++)
        {
            Edge &e=G[u][i];
            if(e.cap>0&&level[e.to]==-1)
            {
                level[e.to]=level[u]+1;
                qi.push(e.to);
            }
        }
    }
}

int dfs(int u,int t,int f)
{
    int d;
    if(u==t) return f;
    for(int &i=iter[u];i<G[u].size();i++)
    {
        Edge &e=G[u][i];
        if(e.cap>0&&level[u]==level[e.to]-1)
        {
            d=dfs(e.to,t,min(f,e.cap));
            if(d>0)
            {
                e.cap-=d;
                G[e.to][e.rev].cap+=d;
                return d;
            }
        }
    }
    return 0;
}

int dinic(int s,int t)
{
    int flow,f;
    flow=0;
    while(1)
    {
        bfs(s);
        if(level[t]==-1) break;
        memset(iter,0,sizeof(iter));
        while((f=dfs(s,t,INF))!=0) flow+=f;
    }
    return flow;
}

int main()
{

    //freopen("in.txt","r",stdin);

    int n,f,d,fi,di,p,i,j;
    while(scanf("%d %d %d",&n,&f,&d)!=EOF)  //超级源点为0,超级汇点为d+300+1
    {                                       //食物编号:1~f  牛编号:1+100~n+100,1+200~n+200  饮料编号:1+300~d+300
        for(i=1;i<=f;i++)  add_edge(0,i,1); //添加超级源点到食物的边
        for(i=1;i<=n;i++)
        {
            scanf("%d %d",&fi,&di);
            for(j=0;j<fi;j++)
            {
                scanf(" %d",&p);
                add_edge(p,i+100,1);  //添加食物到牛的边
            }
            add_edge(i+100,i+200,1);
            for(j=0;j<di;j++)
            {
                scanf(" %d",&p);
                add_edge(i+200,p+300,1);  //添加牛到饮料的边
            }
        }
        for(i=1;i<=d;i++) add_edge(i+300,d+300+1,1); //添加饮料到超级汇点的边

        printf("%d\n",dinic(0,d+300+1));

        for(i=0;i<=d+300+1;i++) G[i].clear();
    }
    return 0;
}

题目( http://poj.org/problem?id=3281):

Dining
Time Limit: 2000MS Memory Limit: 65536K
   

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:  NF, 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.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值