POJ 3281 Dining

来源:互联网 发布:java web文件上传组件 编辑:程序博客网 时间:2024/06/15 18:20

题意:为n头牛准备了f种食物d种饮料,每头牛都有各自喜欢的食物和饮料,而每种食物或饮料只能分配给一头牛,最多能有多少头牛可以同时得到喜欢的食物和饮料

解题思路:最大流.如果只分配食物的话,那么用二分图最大匹配就可以解决,但是这道题是同时给一头牛分配所喜欢的食物和饮料,所以可以将食物和饮料所对应的两个匹配通过下面的方法联合起来求解。1.图的顶点在食物对应的匹配中的食物和牛,饮料对应匹配中的饮料和牛之外还有一个源点s和一个汇点t;2.在两个匹配相同的牛之间连一条线,在s和所有食物,t和所有饮料之间连一条边;3.边的方向为s->食物->牛->牛->饮料->t,各边的容量全部为1。每一条s->t路径都对应一个牛的食物和饮料的分配方案,我们把食物所对应的牛和饮料所对应的牛拆成两个顶点。之间连一条容量为1的边,这样就保证了一头牛不会被分配多组食物和饮料,所以就转化为了求最大流问题。这里用的Dinic算法求最大流

代码:

#include <iostream>#include <algorithm>#include <cstring>#include <string>#include <cstdio>#include <cmath>#include <vector>#include <queue>using namespace std;#define INF 0x3f3f3f3fint n,f,d;int likef[110][110],liked[110][110];//分别对应食物、饮料的喜好//用于表示边的结构体(终点、容量、反向边)struct edge{    int to,cap,rev;};vector<edge> G[1000010];//图的邻接表表示int level[1000010],iter[1000010];//level顶点到源点的距离编号,iter当前弧,在其之前的边已经没有用了//向图中增加一条从from到to的容量为cap的边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});}//通过bfs计算从源点出发的距离编号void bfs(int s){    memset(level,-1,sizeof(level));    queue<int> que;    level[s]=0;    que.push(s);    while(!que.empty())    {        int v=que.front();que.pop();        for(int i=0;i<G[v].size();i++)        {            edge &e=G[v][i];            if(e.cap>0&&level[e.to]<0)            {                level[e.to]=level[v]+1;                que.push(e.to);            }        }    }}//通过dfs寻找增广路int dfs(int v,int t,int f){    if(v==t)return f;    for(int &i=iter[v];i<G[v].size();i++)    {        edge &e=G[v][i];        if(e.cap>0&&level[v]<level[e.to])        {            int 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;}//求解从s到t的最大流int max_flow(int s,int t){    int flow=0;    for(;;)    {        bfs(s);        if(level[t]<0)return flow;        memset(iter,0,sizeof(iter));        int f;        while((f=dfs(s,t,INF))>0)        {            flow+=f;        }    }}int main(){    while(scanf("%d%d%d",&n,&f,&d)==3)    {        memset(liked,0,sizeof(liked));        memset(likef,0,sizeof(likef));        for(int i=0;i<n;i++)        {            int ff,dd;            scanf("%d%d",&ff,&dd);            int fx,dx;            while(ff--)            {                scanf("%d",&fx);                liked[i][fx-1]=1;            }            while(dd--)            {                scanf("%d",&dx);                likef[i][dx-1]=1;            }        }        /*0~n-1 食物一侧的牛          n~2n-1 饮料一侧的牛          2n~2n+f-1 食物          2n+f~2n+f+d-1 饮料        */        int s=n*2+f+d,t=s+1;//源点、汇点        //在s与食物之间连边        for(int i=0;i<f;i++)        {            add_edge(s,n*2+i,1);        }        //在饮料与t之间连边        for(int i=0;i<d;i++)        {            add_edge(n*2+f+i,t,1);        }        for(int i=0;i<n;i++)        {            //在食物一侧的牛和在饮料一侧的牛连边            add_edge(i,n+i,1);            //在牛和所喜欢的食物或饮料之间连边            for(int j=0;j<f;j++)            {                if(liked[i][j])add_edge(n*2+j,i,1);            }            for(int j=0;j<d;j++)            {                if(likef[i][j])add_edge(n+i,n*2+f+j,1);            }        }        printf("%d\n",max_flow(s,t));    }    return 0;}