图论 || 最大流(牛)

Dining

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: N, F, 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

有一群牛,还有一些牛喜欢的食物和喜欢的饮料,不过这些牛都很特别,他们不会与别的牛吃同一种食物或者饮料,现在约翰拿了一些食物和饮料,同时他也知道这些牛喜欢的食物和饮料的种类,求出来最多能让多少头牛吃上食物还并且喝饮料。

输入的第一行是N F D,分别是牛,食物,饮料的个数。下面N行,每行开始的前两个数分别是Fi,Di表示第i头牛喜欢的食物和饮料的个数,紧跟着输入Fi种食物和Di种饮料。


建图:起点 — 食物 — 牛 — 饮料 — 终点,求最多能在图中找到从起点到终点的路径的数量

起点和终点保证经过每种食物和饮料的路径只能有一条

可能会发生以下情况,错误地将同一头牛的两种选择记为两次


把一头牛拆为两个点,即一同牛只能有一条路径经过

#include<stdio.h>
#include<vector>
#include<algorithm>
#include<string.h>
#include<iostream>
using namespace std;
int n,fn,dn;
struct Edge{
	int t;
	int k;//标志这条边能不能通过 
	int re;
};
vector<Edge> g[410];//vector< vector<Edge> > g(410);
//若vector<int> a(10);此时用a.push_back(1) ,则a[11]=1;
//如果vector<int>a;此时a[3]=1; 会出错 
bool used[410];
void add(int f,int t){
	Edge e;
	e.t=t;
	e.k=1;
	e.re=g[t].size();
	g[f].push_back(e);
	e.t=f;
	e.k=0;
	e.re=g[f].size()-1;
	g[t].push_back(e);
}
bool find(int s,int v){
	if(s==v)return true;
	for(int i=0;i<g[s].size();i++){
		Edge e=g[s][i];
		if(!used[e.t]&&e.k==1){
			used[e.t]=1;
			if(find(e.t,v)){
				g[e.t][e.re].k=1;//已经选择的边k值变为0避免重复选择相同的路径,同时为了能回溯做出另外的路径选择将其反向边变为1 
				g[s][i].k=0;//!!!这里不能用e.k=0
				return true;
			}
		}
	}
	return false;
}
int main(){

	scanf("%d%d%d",&n,&fn,&dn);
	for(int j=1;j<=fn;j++){
		add(0,j);
		//起点编号为0,连接起点和所有食物 
	}
	for(int i=fn+1;i<=fn+n;i++){
		add(i,i+n);
		//连接一头牛的两个点 
	}
	for(int j=1;j<=dn;j++){
		add(fn+n+n+j,402);
		//终点编号402,连接所有饮料和终点 
	}
	int ffn,ddn;
	for(int i=1;i<=n;i++){
		scanf("%d%d",&ffn,&ddn);
		int a;
		while(ffn--){
			scanf("%d",&a);
			add(a,fn+i);
		}
		while(ddn--){
			scanf("%d",&a);
			add(fn+n+i,fn+n+n+a);
		}
	}
	int ans=0;
	while(1){
		memset(used,0,sizeof(used));
		used[0]=1;
		if(find(0,402))ans++;
		else break;
	}
	printf("%d\n",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/bekote/article/details/80092896