POJ 3281:Dining

来源:互联网 发布:网络层有哪些功能 编辑:程序博客网 时间:2024/06/06 01:45

题目链接:http://poj.org/problem?id=3281



题目翻译:农夫约翰为它的牛准备了F种食物,D种饮料,每头牛有自己

喜欢的食物和饮料,而每种食物只能分配给一头牛,最多能有多少头牛

可以同时得到喜欢的食物和饮料?


解题思路:

构建网络流:

边的方向   S(超级源点)->食物(F种食物)->牛->牛->饮料(D种饮料)

其中牛到牛是拆点,而且这个题目必须拆点,这个题目是最大流题目,

它属于顶点上有容量限制的最大流,对于容量有限制的顶点,我们必须

要拆点,这个边的容量就是顶点的容量。和POJ 3436:ACM Computer Factory

是一样的,都要拆点,原来我还不太明白为什么非要拆点,但是看了挑战程序

设计之后明白了,拆点之后就达到了限流的作用。否则对于一头牛它有可能

会得到多于一组的食物和饮料的搭配。



AC代码:

#include <iostream>#include <stdio.h>#include <string.h>#include <queue>#include <vector>using namespace std;const int maxn = 500;const int INF = 0x3f3f3f3f;int N,F,D;   ///F种食物,D种饮料,N头牛int S,T;     ///超级源点,超级汇点。int cap[maxn][maxn];  ///容量int level[maxn],vis[maxn];int maxFlow;///广搜分层bool bfs(){    queue<int>qu;    memset(level,-1,sizeof(level));    level[S] = 0;    qu.push(S);    while(!qu.empty())    {        int u = qu.front();        qu.pop();        for(int i = 0; i <= T; i++)        {            if(level[i]==-1 && cap[u][i]>0)            {                level[i] = level[u] + 1;                if(i == T)                    return true;                qu.push(i);            }        }    }    return false;}///求增广路径void dfs(){    int u,v,cur,i;    deque<int>qu;  ///双端队列,当作栈用    memset(vis,0,sizeof(vis));    vis[S] = 1;    qu.push_back(S);    while(!qu.empty())    {        cur = qu.back();        if(cur == T)        {            for(i = 1; i < (int)qu.size(); i++)            {                u = qu[i-1];                v = qu[i];                cap[u][v] -= 1;                cap[v][u] += 1;                vis[v] = 0;   ///直接取消标记            }            maxFlow += 1;  ///图中有权值为1或0的边,座椅一条路中的最大最小流量都是1.            qu.clear();            qu.push_back(S);              ///这种情况其实每次都是从S开始回溯的,因为找到一条路后,这整条路就断了。        }        else        {            for(i = 1; i <= T; i++)            {                if(cap[cur][i]>0 && level[i] == level[cur]+1 && vis[i]==0)                {                    vis[i] = 1;                    qu.push_back(i);                    break;                }            }            if(i > T)                qu.pop_back();        }    }}void dinic(){    maxFlow = 0;    while(bfs())    {        dfs();    }    printf("%d\n",maxFlow);}int main(){    while(~scanf("%d%d%d",&N,&F,&D)) ///N牛的数量,F食物种类,D饮料种类    {        S = 0;                       ///超级源点        T = 2*N+F+D+1;               ///超级汇点        ///0是超级源点,1~2*N是牛拆点,2*N+1~2*N+F是食物,2*N+F+1~2*N+F+D是饮料        memset(cap,0,sizeof(cap));        ///超级源点向每种食物引一条边        for(int i = 2*N+1; i <= 2*N+F; i++)        {            cap[S][i] = 1;        }        ///每种饮料向超级汇点引一条边        for(int i = 2*N+F+1; i <= 2*N+F+D; i++)        {            cap[i][T] = 1;        }        ///每个牛拆成两个点,来达到限流的目的        for(int i = 1; i <= N; i++)        {            cap[i][i+N] = 1;        }        int food,drink;        for(int i = 1; i <= N; i++)        {            scanf("%d%d",&food,&drink);            int u;            for(int j = 1; j <= food; j++)            {                scanf("%d",&u);  ///食物向原来牛的点引边,食物编号2*N+u                u = 2*N+u;                cap[u][i] = 1;            }            for(int j = 1; j <= drink; j++)            {                scanf("%d",&u);  ///从牛拆出的点向饮料引边,牛的编号i+N,音量编号2*N+F+u                u = 2*N+F+u;                cap[i+N][u] = 1;            }        }        dinic();    }    return 0;}