码迷,mamicode.com
首页 > 其他好文 > 详细

POJ 3281 Dining (拆点)【最大流】

时间:2018-11-23 17:28:27      阅读:173      评论:0      收藏:0      [点我收藏+]

标签:sizeof   set   while   分配   mil   +=   amp   题目   turn   

<题目链接>

题目大意:

有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\n", Dinic(0, sect));
107     }
108     return 0;
109 }

 

 

2018-11-23

POJ 3281 Dining (拆点)【最大流】

标签:sizeof   set   while   分配   mil   +=   amp   题目   turn   

原文地址:https://www.cnblogs.com/00isok/p/10008683.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!