PIGS
Description Mirko works on a pig farm that consists of M locked pig-houses and Mirko can't unlock any pighouse because he doesn't have the keys. Customers come to the farm one after another. Each of them has keys to some pig-houses and wants to buy a certain number of pigs.
All data concerning customers planning to visit the farm on that particular day are available to Mirko early in the morning so that he can make a sales-plan in order to maximize the number of pigs sold. More precisely, the procedure is as following: the customer arrives, opens all pig-houses to which he has the key, Mirko sells a certain number of pigs from all the unlocked pig-houses to him, and, if Mirko wants, he can redistribute the remaining pigs across the unlocked pig-houses. An unlimited number of pigs can be placed in every pig-house. Write a program that will find the maximum number of pigs that he can sell on that day. Input The first line of input contains two integers M and N, 1 <= M <= 1000, 1 <= N <= 100, number of pighouses and number of customers. Pig houses are numbered from 1 to M and customers are numbered from 1 to N.
The next line contains M integeres, for each pig-house initial number of pigs. The number of pigs in each pig-house is greater or equal to 0 and less or equal to 1000. The next N lines contains records about the customers in the following form ( record about the i-th customer is written in the (i+2)-th line): A K1 K2 ... KA B It means that this customer has key to the pig-houses marked with the numbers K1, K2, ..., KA (sorted nondecreasingly ) and that he wants to buy B pigs. Numbers A and B can be equal to 0. Output The first and only line of the output should contain the number of sold pigs.
Sample Input 3 3 3 1 10 2 1 2 2 2 1 3 3 1 2 6 Sample Output 7 Source |
[Submit] [Go Back] [Status] [Discuss]
网络流——流量传递类型。
四月份的时候曾经写过一遍,但是已然忘光了,所以只得重新来过。
错误的建图方法
看到题之后有个简单的建图想法,就是设立M*N个猪圈点,以及N个商人点,如下建图:
源点向第一排M个猪圈点连容量为初始猪数目的边,代表一开始猪圈中最多存在的猪。
每排M个猪圈点中,A个能被打开的猪圈点向商人点连容量为无穷的边,代表可以被打开。
每排的商人点向汇点连容量为商人购买数目B的边,表示这个人的最大购买限度。
每排的商人点向下一排的A个点(这A个点还是本排商人能打开的猪圈)连无穷边,代表猪可以自由转移。
当然,因为猪也可以待在猪圈里不动,所以每排的各个猪圈向下一排的该猪圈连无穷边。
这样跑最大流,显然可以得到最优解,但是点的数量是O(N*M)的,而边的数量是……(懒得想了),果不其然地TLE了。
#include <cstdio> #include <cstring> inline int get_c(void) { static const int siz = 1024; static char buf[siz]; static char *head = buf + siz; static char *tail = buf + siz; if (head == tail) fread(head = buf, 1, siz, stdin); return *head++; } inline int get_i(void) { register int ret = 0; register int neg = false; register int bit = get_c(); for (; bit < 48; bit = get_c()) if (bit == '-')neg ^= true; for (; bit > 47; bit = get_c()) ret = ret * 10 + bit - 48; return neg ? -ret : ret; } template <class T> inline T min(T a, T b) { return a < b ? a : b; } const int inf = 2e9; const int maxn = 500005; int n, m; int s, t; int edges; int hd[maxn]; int to[maxn]; int nt[maxn]; int fl[maxn]; inline void add(int u, int v, int f) { //printf("add %d %d %d ", u, v, f); nt[edges] = hd[u]; to[edges] = v; fl[edges] = f; hd[u] = edges++; nt[edges] = hd[v]; to[edges] = u; fl[edges] = 0; hd[v] = edges++; } int dep[maxn]; inline bool bfs(void) { static int que[maxn]; static int head, tail; memset(dep, 0, sizeof(dep)); head = 0, tail = 0; que[tail++] = s; dep[s] = 1; while (head != tail) { int u = que[head++], v; for (int i = hd[u]; ~i; i = nt[i]) if (!dep[v = to[i]] && fl[i]) { dep[v] = dep[u] + 1; que[tail++] = v; } } return dep[t] != 0; } int dfs(int u, int f) { if (u == t || !f) return f; int used = 0, flow, v; for (int i = hd[u]; ~i; i = nt[i]) if (dep[v = to[i]] == dep[u] + 1 && fl[i]) { flow = dfs(v, min(f - used, fl[i])); used += flow; fl[i] -= flow; fl[i^1] += flow; if (used == f) return f; } if (!used) dep[u] = 0; return used; } inline int maxFlow(void) { int maxFlow = 0, newFlow; while (bfs()) while (newFlow = dfs(s, inf)) maxFlow += newFlow; return maxFlow; } signed main(void) { n = get_i(); m = get_i(); s = 0, t = (n + 1) * m + 1; memset(hd, -1, sizeof(hd)); for (int i = 1; i <= n; ++i) { int pigs = get_i(); add(s, i, pigs); } for (int i = 1; i < m; ++i) for (int j = 1; j <= n; ++j) add((n + 1)*(i - 1) + j, (n + 1)*i + j, inf); for (int i = 1; i <= m; ++i) { int k = get_i(); for (int j = 1; j <= k; ++j) { int p = get_i(); add((n + 1)*(i - 1) + p, (n + 1)*i, inf); add((n + 1)*i, (n + 1)*i + p, inf); } add((n + 1)*i, t, get_i()); } printf("%d ", maxFlow()); }
正确的建图方法
设置一个源点,向M个猪圈点连初始数目的边,代表一开始的猪的数目。
然后想办法简化猪圈之间的转移,着重商人能得到的猪的来源,考虑直接在商人之间进行转移。
易知,如果商人X和商人Y,有X比Y先来,且X和Y有公共的猪圈的钥匙,那么Y能享受X能到达的所有猪圈。
用last_i表示上一个可以打开i猪圈的点,初始last_i=i猪圈初始点。
一个商人,如果有猪圈k的钥匙,那么last_k的所有流量都可以向这个商人转移,所有从last_k向商人连边,同时把last_k设为该商人。
商人点向汇点连最大购买数量的边。
跑最大流即可,点数O(N+M),边数O(N+M),小菜一碟了。
1 #include <cstdio> 2 #include <cstring> 3 4 inline int get_c(void) 5 { 6 static const int siz = 1024; 7 8 static char buf[siz]; 9 static char *head = buf + siz; 10 static char *tail = buf + siz; 11 12 if (head == tail) 13 fread(head = buf, 1, siz, stdin); 14 15 return *head++; 16 } 17 18 inline int get_i(void) 19 { 20 register int ret = 0; 21 register int neg = false; 22 register int bit = get_c(); 23 24 for (; bit < 48; bit = get_c()) 25 if (bit == '-')neg ^= true; 26 27 for (; bit > 47; bit = get_c()) 28 ret = ret * 10 + bit - 48; 29 30 return neg ? -ret : ret; 31 } 32 33 template <class T> 34 inline T min(T a, T b) 35 { 36 return a < b ? a : b; 37 } 38 39 const int inf = 2e9; 40 const int maxn = 500005; 41 42 int n, m; 43 int s, t; 44 int edges; 45 int hd[maxn]; 46 int to[maxn]; 47 int nt[maxn]; 48 int fl[maxn]; 49 50 inline void add(int u, int v, int f) 51 { //printf("add %d %d %d ", u, v, f); 52 nt[edges] = hd[u]; to[edges] = v; fl[edges] = f; hd[u] = edges++; 53 nt[edges] = hd[v]; to[edges] = u; fl[edges] = 0; hd[v] = edges++; 54 } 55 56 int dep[maxn]; 57 58 inline bool bfs(void) 59 { 60 static int que[maxn]; 61 static int head, tail; 62 63 memset(dep, 0, sizeof(dep)); 64 head = 0, tail = 0; 65 que[tail++] = s; 66 dep[s] = 1; 67 68 while (head != tail) 69 { 70 int u = que[head++], v; 71 for (int i = hd[u]; ~i; i = nt[i]) 72 if (!dep[v = to[i]] && fl[i]) 73 { 74 dep[v] = dep[u] + 1; 75 que[tail++] = v; 76 } 77 } 78 79 return dep[t] != 0; 80 } 81 82 int dfs(int u, int f) 83 { 84 if (u == t || !f) 85 return f; 86 87 int used = 0, flow, v; 88 89 for (int i = hd[u]; ~i; i = nt[i]) 90 if (dep[v = to[i]] == dep[u] + 1 && fl[i]) 91 { 92 flow = dfs(v, min(f - used, fl[i])); 93 94 used += flow; 95 fl[i] -= flow; 96 fl[i^1] += flow; 97 98 if (used == f) 99 return f; 100 } 101 102 if (!used) 103 dep[u] = 0; 104 105 return used; 106 } 107 108 inline int maxFlow(void) 109 { 110 int maxFlow = 0, newFlow; 111 112 while (bfs()) 113 while (newFlow = dfs(s, inf)) 114 maxFlow += newFlow; 115 116 return maxFlow; 117 } 118 119 int last[maxn]; 120 121 signed main(void) 122 { 123 n = get_i(); 124 m = get_i(); 125 126 s = 0, t = n + m + 1; 127 128 memset(hd, -1, sizeof(hd)); 129 130 for (int i = 1; i <= n; ++i) 131 add(s, i, get_i()), last[i] = i; 132 133 for (int i = 1; i <= m; ++i) 134 { 135 for (int j = get_i(), k; j--; ) 136 k = get_i(), add(last[k], n + i, inf), last[k] = n + i; 137 add(n + i, t, get_i()); 138 } 139 140 printf("%d ", maxFlow()); 141 }
@Author: YouSiki