题目链接: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;
}