1. 程式人生 > >POJ 3281 Dining (拆點)【最大流】

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

empty bool color problem col 多個 16px edge 找到

<題目鏈接>

題目大意:

有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 (拆點)【最大流】