题目链接:https://vjudge.net/problem/POJ-3281

Dining
Time Limit: 2000MS   Memory Limit: 65536K
Total Submissions: 20017   Accepted: 8901

Description

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: NF, 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.

Source

题意:

有N头牛, F个食物, D个饮料。每头牛只吃或者喝自己喜欢的食物或饮料,问:怎样分配,使得尽量多的牛能够获得一个食物或一个饮料?

题解:

题目要求就是要食物、饮料与牛进行匹配,但是又不能用匹配算法,因为有两种匹配。因而可以利用网络流:食物放左边,牛放中间,饮料放右边,然后最左边加个超级源点,最右边加个超级汇点。意思就是要:先将食物与牛进行匹配,然后再用匹配后的牛与饮料进行匹配。

1.超级源点与食物相连,且边的容量为1,表明每种食物只提供一份。

2.将每头牛拆成两点,左边与食物相连,边的容量为1,表明最多只能提供一份食物。右边与饮料相连,边的容量为1,表明最多只能提供一份饮料。然后内部相连,边的容量为1, 表明有“1头牛”。

3.每个饮料与超级汇点相连,且边的容量为1,表明每种饮料只提供一份。

4.建图完毕,求最大流即可。

代码如下:

 #include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <string>
#include <set>
using namespace std;
typedef long long LL;
const int INF = 2e9;
const LL LNF = 9e18;
const int mod = 1e9+;
const int MAXN = 5e2+; int maze[MAXN][MAXN];
int gap[MAXN], dis[MAXN], pre[MAXN], cur[MAXN];
int flow[MAXN][MAXN]; int sap(int start, int end, int nodenum)
{
memset(cur, , sizeof(cur));
memset(dis, , sizeof(dis));
memset(gap, , sizeof(gap));
memset(flow, , sizeof(flow));
int u = pre[start] = start, maxflow = , aug = INF;
gap[] = nodenum; while(dis[start]<nodenum)
{
loop:
for(int v = cur[u]; v<nodenum; v++)
if(maze[u][v]-flow[u][v]> && dis[u] == dis[v]+)
{
aug = min(aug, maze[u][v]-flow[u][v]);
pre[v] = u;
u = cur[u] = v;
if(v==end)
{
maxflow += aug;
for(u = pre[u]; v!=start; v = u, u = pre[u])
{
flow[u][v] += aug;
flow[v][u] -= aug;
}
aug = INF;
}
goto loop;
} int mindis = nodenum-;
for(int v = ; v<nodenum; v++)
if(maze[u][v]-flow[u][v]> && mindis>dis[v])
{
cur[u] = v;
mindis = dis[v];
}
if((--gap[dis[u]])==) break;
gap[dis[u]=mindis+]++;
u = pre[u];
}
return maxflow;
} int main()
{
int N, F, D;
while(scanf("%d%d%d",&N,&F,&D)!=EOF)
{
memset(maze, , sizeof(maze));
for(int i = ; i<N; i++)
{
int f, d, v;
scanf("%d%d", &f,&d);
while(f--)
{
scanf("%d", &v);
v--;
maze[v][F+D+i] = ; //food --> cow
}
while(d--)
{
scanf("%d", &v);
v--;
maze[F+D+N+i][F+v] = ; //cow' --> drink
}
maze[F+D+i][F+D+N+i] = ; // cow --> cow'
} int start = F+D+*N, end = F+D+*N+;
for(int i = ; i<F; i++) maze[start][i] = ; //超级源点 --> food
for(int i = F; i<F+D; i++) maze[i][end] = ; //drink --> 超级汇点 int ans = sap(start, end, F+D+*N+);
printf("%d\n", ans);
}
}

没有cur优化的sap:

 #include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <string>
#include <set>
using namespace std;
typedef long long LL;
const int INF = 2e9;
const LL LNF = 9e18;
const int mod = 1e9+;
const int MAXN = 5e2+; int maze[MAXN][MAXN];
int gap[MAXN], dis[MAXN], pre[MAXN];
int flow[MAXN][MAXN]; int sap(int start, int end, int nodenum)
{
memset(dis, , sizeof(dis));
memset(gap, , sizeof(gap));
memset(flow, , sizeof(flow));
int u = pre[start] = start, maxflow = , aug = INF;
gap[] = nodenum; while(dis[start]<nodenum)
{
loop:
for(int v = ; v<nodenum; v++)
if(maze[u][v]-flow[u][v]> && dis[u] == dis[v]+)
{
aug = min(aug, maze[u][v]-flow[u][v]);
pre[v] = u;
u = v;
if(v==end)
{
maxflow += aug;
for(u = pre[u]; v!=start; v = u, u = pre[u])
{
flow[u][v] += aug;
flow[v][u] -= aug;
}
aug = INF;
}
goto loop;
} int mindis = nodenum-;
for(int v = ; v<nodenum; v++)
if(maze[u][v]-flow[u][v]>)
mindis = min(mindis, dis[v]); if((--gap[dis[u]])==) break;
gap[dis[u]=mindis+]++;
u = pre[u];
}
return maxflow;
} int main()
{
int N, F, D;
while(scanf("%d%d%d",&N,&F,&D)!=EOF)
{
memset(maze, , sizeof(maze));
for(int i = ; i<N; i++)
{
int f, d, v;
scanf("%d%d", &f,&d);
while(f--)
{
scanf("%d", &v);
v--;
maze[v][F+D+i] = ; //food --> cow
}
while(d--)
{
scanf("%d", &v);
v--;
maze[F+D+N+i][F+v] = ; //cow' --> drink
}
maze[F+D+i][F+D+N+i] = ; // cow --> cow'
} int start = F+D+*N, end = F+D+*N+;
for(int i = ; i<F; i++) maze[start][i] = ; //超级源点 --> food
for(int i = F; i<F+D; i++) maze[i][end] = ; //drink --> 超级汇点 int ans = sap(start, end, F+D+*N+);
printf("%d\n", ans);
}
}

没有cur和gap优化的sap:

 #include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <string>
#include <set>
using namespace std;
typedef long long LL;
const int INF = 2e9;
const LL LNF = 9e18;
const int mod = 1e9+;
const int MAXN = 5e2+; int maze[MAXN][MAXN];
int dis[MAXN], pre[MAXN];
int flow[MAXN][MAXN]; int sap(int start, int end, int nodenum)
{
memset(dis, , sizeof(dis));
memset(flow, , sizeof(flow));
int u = pre[start] = start, maxflow = , aug = INF; while(dis[start]<nodenum)
{
loop:
for(int v = ; v<nodenum; v++)
if(maze[u][v]-flow[u][v]> && dis[u] == dis[v]+)
{
aug = min(aug, maze[u][v]-flow[u][v]);
pre[v] = u;
u = v;
if(v==end)
{
maxflow += aug;
for(u = pre[u]; v!=start; v = u, u = pre[u])
{
flow[u][v] += aug;
flow[v][u] -= aug;
}
aug = INF;
}
goto loop;
} int mindis = nodenum-;
for(int v = ; v<nodenum; v++)
if(maze[u][v]-flow[u][v]>)
mindis = min(mindis, dis[v]); dis[u]=mindis+;
u = pre[u];
}
return maxflow;
} int main()
{
int N, F, D;
while(scanf("%d%d%d",&N,&F,&D)!=EOF)
{
memset(maze, , sizeof(maze));
for(int i = ; i<N; i++)
{
int f, d, v;
scanf("%d%d", &f,&d);
while(f--)
{
scanf("%d", &v);
v--;
maze[v][F+D+i] = ; //food --> cow
}
while(d--)
{
scanf("%d", &v);
v--;
maze[F+D+N+i][F+v] = ; //cow' --> drink
}
maze[F+D+i][F+D+N+i] = ; // cow --> cow'
} int start = F+D+*N, end = F+D+*N+;
for(int i = ; i<F; i++) maze[start][i] = ; //超级源点 --> food
for(int i = F; i<F+D; i++) maze[i][end] = ; //drink --> 超级汇点 int ans = sap(start, end, F+D+*N+);
printf("%d\n", ans);
}
}

POJ3281 Dining —— 最大流 + 拆点的更多相关文章

  1. [poj3281]Dining(最大流+拆点)

    题目大意:有$n$头牛,$f$种食物和$d$种饮料,每种食物或饮料只能供一头牛享用,且每头牛只享用一种食物和一种饮料.每头牛都有自己喜欢的食物种类列表和饮料种类列表,问最多能使几头牛同时享用到自己喜欢 ...

  2. POJ3281 Dining 最大流

    题意:有f种菜,d种饮品,每个牛有喜欢的一些菜和饮品,每种菜只能被选一次,饮品一样,问最多能使多少头牛享受自己喜欢的饮品和菜 分析:建边的时候,把牛拆成两个点,出和入 1,源点向每种菜流量为1 2,每 ...

  3. poj3281 Dining 最大流(奇妙的构图)

    我是按照图论500题的文档来刷题的,看了这题怎么也不觉得这是最大流的题目.这应该是题目做得太少的缘故. 什么是最大流问题?最大流有什么特点? 最大流的特点我觉得有一下几点: 1.只有一个起点.一个终点 ...

  4. POJ 3281 Dining(最大流+拆点)

    题目链接:http://poj.org/problem?id=3281 题目大意:农夫为他的 N (1 ≤ N ≤ 100) 牛准备了 F (1 ≤ F ≤ 100)种食物和 D (1 ≤ D ≤ 1 ...

  5. POJ3281:Dining(dinic+拆点)

    题目链接:http://poj.org/problem?id=3281 PS:刷够网络流了,先这样吧,之后再刷,慢慢补. 题意:有F种食物,D种饮料,N头奶牛,只能吃某种食物和饮料(而且只能吃特定的一 ...

  6. POJ3281 Dining(拆点构图 + 最大流)

    题目链接 题意:有F种食物,D种饮料N头奶牛,只能吃某种食物和饮料(而且只能吃特定的一份) 一种食物被一头牛吃了之后,其余牛就不能吃了第一行有N,F,D三个整数接着2-N+1行代表第i头牛,前面两个整 ...

  7. <每日一题>Day 9:POJ-3281.Dining(拆点 + 多源多汇+ 网络流 )

    Dining Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 24945   Accepted: 10985 Descript ...

  8. poj 3498 March of the Penguins(最大流+拆点)

    题目大意:在南极生活着一些企鹅,这些企鹅站在一些冰块上,现在要让这些企鹅都跳到同一个冰块上.但是企鹅有最大的跳跃距离,每只企鹅从冰块上跳走时会给冰块造成损害,因此企鹅跳离每个冰块都有次数限制.找出企鹅 ...

  9. poj 2391 Ombrophobic Bovines, 最大流, 拆点, 二分, dinic, isap

    poj 2391 Ombrophobic Bovines, 最大流, 拆点, 二分 dinic /* * Author: yew1eb * Created Time: 2014年10月31日 星期五 ...

随机推荐

  1. 使用sudo,mvn command not found

    一个简单的解决办法是,编辑你当前用户的 .bashrc 文件,添加下面这行内容: alias sudo="sudo env PATH=$PATH" 因为系统预装的 sudo 在编译 ...

  2. UVa10214 Trees in a Wood.

    先算第一象限能看到的树,答案乘以4就是四个象限的数的总数,再加上坐标轴上四棵树,就是总共能看到的树. 树的总数为(2*a+1)*(2*b+1)-1  ←矩形面积除去原点位置 设一棵树的坐标是(x,y) ...

  3. POJ 3468 线段树 成段更新 懒惰标记

    A Simple Problem with Integers Time Limit:5000MS   Memory Limit:131072K Case Time Limit:2000MS Descr ...

  4. Linux java 启动脚本

    #!/bin/bash export LANG=en_US.UTF8 start(){ ulimit -n 65535 #find the jars jar_lib=`ls -1 lib/*.jar` ...

  5. 标准C程序设计七---02

    Linux应用             编程深入            语言编程 标准C程序设计七---经典C11程序设计    以下内容为阅读:    <标准C程序设计>(第7版) 作者 ...

  6. hdu3078 建层次树+在线LCA算法+排序

    题意:n个点,n-1条边构成无向树,每个节点有权,Q次询问,每次或问从a->b的最短路中,权第k大的值,/或者更新节点a的权, 思路:在线LCA,先dfs生成树0,标记出层数和fa[](每个节点 ...

  7. nginx原配置

    #原配置 server { listen ; server_name localhost; #charset koi8-r; #access_log logs/host.access.log main ...

  8. Servlet(生命周期)

    <?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" ...

  9. 【Java TCP/IP Socket】深入剖析socket——TCP套接字的生命周期

    建立TCP连接      新的Socket实例创建后,就立即能用于发送和接收数据.也就是说,当Socket实例返回时,它已经连接到了一个远程终端,并通过协议的底层实现完成了TCP消息或握手信息的交换. ...

  10. VUE 自定义组件之间的相互通信

    一.自定义组件 1.全局自定义组件 我们在var vm = new Vue({});的上面并列写上Vue.component('自定义组件名',{组件对象});来完成全局自定义组件的声明.示例代码如下 ...