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]; //正向弧(容量为w)
 25     head[u] = cnt++;
 26     edge[cnt].v = u, edge[cnt].cap = 0, edge[cnt].flow = 0,edge[cnt].next = head[v]; //反向弧(容量为0)
 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){  //注意这里的&符号,这样i增加的同时也能改变cur[u]的值,达到更新当前弧的目的
 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){  //f为残余网络中增广路径的最小残量
 83             edge[i].flow += f;    //进行增广
 84             edge[i ^ 1].flow -= f;   //正向流量+f,相当于反向流量-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

转载于:https://www.cnblogs.com/00isok/p/10008683.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值