POJ 3281 Dining (拆点)最大流

Posted 00isok

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了POJ 3281 Dining (拆点)最大流相关的知识,希望对你有一定的参考价值。

<题目链接>

题目大意:

有N头牛,F种食物,D种饮料,每一头牛都有自己喜欢的食物和饮料,且每一种食物和饮料都只有一份,让你分配这些食物和饮料,问最多能使多少头牛同时获得自己喜欢的食物和饮料。

解题分析:

开始还以为是一道匹配问题,后面才知道这是用网络流求解。

首先我们要明确,如果按照源点——>食物——>牛——>饮料——>汇点这样建图,是不符合题目条件的。因为题目要求每头牛只能吃一份食物和饮料,而这样建图,如果一头牛对应多个食物和饮料,那这样那头牛是可以吃多份食物和饮料的,所以我们需要对牛进行拆点,并且拆除的两点之间用容量为1边相连,这样跑最大流的时候就能够限制每头牛最多只能吃一份食物和饮料了。下面是Dinic的模板。

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <queue>
  4 #include <algorithm>
  5 using namespace std;
  6 
  7 #define INF 0x3f3f3f3f
  8 const int maxn = 500;
  9 const int maxm = 1e5; 
 10 
 11 int depth[maxn], vis[maxn],cur[maxn], head[maxn];
 12 int N, F, D;
 13 int sect,cnt;    //sect为汇点
 14 struct Edge {
 15     int v, cap, flow, next;
 16 }edge[maxm];
 17  
 18 void init(){
 19     cnt = 0;
 20     memset(head, -1, sizeof(head));
 21 }
 22  
 23 void add(int u, int v, int w){     //建立双向边
 24     edge[cnt].v = v, edge[cnt].cap = w, edge[cnt].flow = 0,edge[cnt].next = head[u]; 
 25     head[u] = cnt++;
 26     edge[cnt].v = u, edge[cnt].cap = 0, edge[cnt].flow = 0,edge[cnt].next = head[v]; 
 27     head[v] = cnt++;
 28 }
 29  
 30 void getmap(){
 31     int tmp1, tmp2;
 32     for(int i = 1; i <= N; ++i){
 33         scanf("%d%d", &tmp1, &tmp2);
 34         while(tmp1--){
 35             int num;
 36             scanf("%d", &num);
 37             add(2 * N + num, i, 1);//食物和左牛连接
 38         }
 39         while(tmp2--){
 40             int num;
 41             scanf("%d", &num);
 42             add(N + i, 2 * N + F + num, 1);//右牛和饮料连接
 43         }
 44         add(i, i + N, 1);//左牛和右牛连接
 45     }
 46     sect = 2 * N + F + D + 1;
 47     for(int i = 1; i <= F; ++i)
 48         add(0, 2 * N + i, 1);//源点和食物连接
 49     for(int i = 1; i <= D; ++i)
 50         add(2 * N + F + i, 2 * N + F + D + 1, 1);//饮料和超级汇点连接
 51 }
 52  
 53 bool BFS(int st, int ed){    //构建分层图,并且判断增广路径是否存在
 54     queue<int>q;
 55     memset(depth, -1, sizeof(depth));    //将所有点分层,初始化深度为-1
 56     memset(vis, 0 ,sizeof(vis));
 57     q.push(st);
 58     depth[st] = 0;   //源点深度为0
 59     vis[st] = 1;
 60     while(!q.empty()){
 61         int u = q.front();
 62         q.pop();
 63         for(int i = head[u]; i != -1; i = edge[i].next){
 64             Edge &E = edge[i];
 65             if(!vis[E.v] && E.cap > E.flow){
 66                 vis[E.v] = 1;
 67                 depth[E.v] = depth[u] + 1;    //下一个点是当前点的深度+1
 68                 if(E.v == ed) return true;    //找到汇点则直接返回
 69                 q.push(E.v);
 70             }
 71         }
 72     }
 73     return false;    //没有找到通向汇点的增广路径
 74 }
 75  
 76 int DFS(int x, int ed, int val){
 77     if(x == ed || val == 0)
 78         return val;
 79     int flow  = 0, f;
 80     for(int &i = cur[x]; i != -1; i = edge[i].next){   //每次从当前弧开始找
 81         Edge E=edge[i];
 82         if(depth[E.v] == depth[x] + 1 && (f = DFS(E.v, ed, min(val, E.cap - E.flow))) > 0){
 83             edge[i].flow += f;
 84             edge[i ^ 1].flow -= f;
 85             flow += f;
 86             val -= f;
 87             if(val == 0) break;
 88         }
 89     }
 90     return flow;
 91 }
 92  
 93 int Dinic(int st, int ed){
 94     int sumflow = 0;  //最大流
 95     while(BFS(st, ed)){     //判断是否存在增广路
 96         memcpy(cur, head, sizeof(head));    //当前弧优化
 97         sumflow += DFS(st, ed, INF);
 98     }
 99     return sumflow;
100 }
101  
102 int main (){
103     while(scanf("%d%d%d", &N, &F, &D) != EOF){
104         init();
105         getmap();    //建图
106         printf("%d
", Dinic(0, sect));
107     }
108     return 0;
109 }

 

 

2018-11-23

以上是关于POJ 3281 Dining (拆点)最大流的主要内容,如果未能解决你的问题,请参考以下文章

POJ 3281 Dining(最大流)

poj 3281 Dining(网络流+拆点)

POJ 3281 Dining(网络流拆点)

POJ 3281(Dining-网络流拆点)[Template:网络流dinic]

bzoj 1711 [Usaco2007 Open]Dining吃饭&&poj 3281 Dining

Dining POJ - 3281