http://poj.org/problem?id=3281
题意:n头牛f种食物d种饮料,每头牛都有各自喜欢的食物和饮料(不止一种),而每种食物或饮料只能分配给一头牛(数量为1)。问最多能有多少头牛可以同时得到喜欢的食物和饮料?
思路:普通建图都是超级源点与供应相连接,超级汇点与需求相连接。然而这里有两种供应一种消耗,但是只有供应和消耗之间有联系,同种消耗之间没有联系,要想建立一个连贯的图,必须将供应放在中间。于是这样就变成了源点-食物-牛-饮料-汇点这样的层次图。容量设定为1,保证每个饮料和食物只能分配1单位给牛。
接下来才是关键,早就听说必须要拆点,但一直不知道为毛要拆。看别人博客,“源点-food-牛-drink-汇点,这样虽然满足每份food和drink只能给一头牛吃,但是没法解决每头牛只能吃一份的问题。”,也就是说不拆点每头牛有可能吃两份以上。这我就想不通了,最大流算法中不是每次只能找到一条增广路吗,而且每找到一条就加上相应的流量,更何况这里都是容量为1的边,下一条不应该再路过这条边啊,既然不路过,那怎么会吃两个以上食物?模拟一下吧:
红色为已经找到的增广路,找下一条蓝色的增广路时虽然没有路过边,但依然可以经过那个点!注意我图中是按照拆点前的路线走的。问题就出在牛这里!但是为什么会这样?根本原因就是牛想要的食物不止一个,饮料也不止一个,换句话说就是两种供应引起的对道路限制的忽略。以前我做的都是一种供应,所以没有遇到这种问题,这次算是开了眼界了。这样拆点后相当于给牛这个点加了个限制,不让下一条增广路路过这里。
我觉得我说的够详细了吧。
#include <stdio.h>
#include <algorithm>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <queue>
#include <stack>
#include <ctype.h>
using namespace std;
typedef long long LL;
const int N = 1000005;
const int INF = 0x3f3f3f3f;
int head[N], dis[N], cur[N];
bool vis[N];
int n, cnt, f, d;
struct Edge
{
int to, cap, flow, next;
}edge[N];
void add(int u, int v, int w)
{
edge[cnt] = (struct Edge){v, w, 0, head[u]};
head[u] = cnt++;
edge[cnt] = (struct Edge){u, 0, 0, head[v]};
head[v] = cnt++;
}
bool bfs(int start, int endd)
{
memset(dis, -1, sizeof(dis));
memset(vis, false, sizeof(vis));
queue<int>que;
dis[start] = 0;
vis[start] = true;
que.push(start);
while(!que.empty())
{
int u = que.front();
que.pop();
for(int i = head[u]; i != -1; i = edge[i].next)
{
Edge E = edge[i];
if(!vis[E.to] && E.flow<E.cap)
{
dis[E.to] = dis[u]+1;
vis[E.to] = true;
if(E.to == endd) return true;
que.push(E.to);
}
}
}
return false;
}
int dfs(int x, int res, int endd)
{
if(x == endd || res == 0) return res;
int flow = 0, f;
for(int& i = cur[x]; i != -1; i = edge[i].next)
{
Edge E = edge[i];
if(dis[E.to] == dis[x]+1)
{
f = dfs(E.to, min(res, E.cap-E.flow), endd);
if(f>0)
{
edge[i].flow += f;
edge[i^1].flow -= f;
flow += f;
res -= f;
if(res == 0) break;
}
}
}
return flow;
}
int max_flow(int start, int endd)
{
int flow = 0;
while(bfs(start, endd))
{
memcpy(cur, head, sizeof(head));
flow += dfs(start, INF, endd);
}
return flow;
}
void init()
{
cnt = 0;
memset(head, -1, sizeof(head));
}
void getmap()
{
int s, t, fnum, dnum;
for(int i = 1; i <= n; i++)
{
scanf("%d%d", &fnum, &dnum);
while(fnum--)//食物
{
scanf("%d", &s);
add(2*n+s, i, 1);
}
while(dnum--)//饮料
{
scanf("%d", &t);
add(n+i, 2*n+f+t, 1);
}
add(i, n+i, 1);
}
for(int i = 1; i <= f; i++)
add(0, 2*n+i, 1);
for(int i = 1; i <= d; i++)
add(2*n+f+i, 2*n+f+d+1, 1);
}
int main()
{
// freopen("in.txt", "r", stdin);
while(~scanf("%d%d%d", &n, &f, &d))
{
init();
getmap();
int ans = max_flow(0, 2*n+f+d+1);
printf("%d\n", ans);
}
return 0;
}