传送门:https://www.luogu.org/problemnew/show/P1231
这是一道很不错的网络流入门题,关键在于如何建图。
首先,我们将练习册和源点连一条边权为1的边,然后若书 i 和练习册 j 可以配套,就将连一条从练习册 j 到书 i 边,当然边权还是1。同理,答案和书也是如此,最后再将答案和汇点连一条边权为1的边。
但是这么写还是会有点问题,因为经过一本书的路径可能与很多条,书就被使用了多次,显然不符合题意。这时候我们可以将书 i 拆成书 i1 和 i2,i1 和练习册连边,i2 和答案连边,这样就保证没一本书之用过一次了。
建好图后跑最大流就行了。
1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #include<cmath> 5 #include<cstring> 6 #include<cstdlib> 7 #include<cctype> 8 #include<stack> 9 #include<queue> 10 #include<vector> 11 using namespace std; 12 #define enter printf("\n") 13 #define space printf(" ") 14 #define Mem(a) memset(a, 0, sizeof(a)) 15 typedef long long ll; 16 typedef double db; 17 const int INF = 0x3f3f3f3f; 18 const db eps = 1e-8; 19 const int maxn = 1e4 + 5; 20 inline ll read() 21 { 22 ll ans = 0; 23 char ch = getchar(), last = ' '; 24 while(!isdigit(ch)) {last = ch; ch = getchar();} 25 while(isdigit(ch)) 26 { 27 ans = ans * 10 + ch - '0'; ch = getchar(); 28 } 29 if(last == '-') ans = -ans; 30 return ans; 31 } 32 inline void write(ll x) 33 { 34 if(x < 0) x = -x, putchar('-'); 35 if(x >= 10) write(x / 10); 36 putchar(x % 10 + '0'); 37 } 38 39 int t, n1, n2, n3; 40 41 struct Edge 42 { 43 int from, to, cap, flow; 44 }; 45 vector<Edge> edges; 46 vector<int> G[maxn << 2]; 47 void addEdge(int from, int to) 48 { 49 edges.push_back((Edge){from, to, 1, 0}); 50 edges.push_back((Edge){to, from, 0, 0}); 51 int sz = edges.size(); 52 G[from].push_back(sz - 2); 53 G[to].push_back(sz - 1); 54 } 55 56 int dis[maxn << 2]; 57 bool vis[maxn << 2]; 58 bool bfs() 59 { 60 Mem(vis); 61 queue<int> q; 62 q.push(0); vis[0] = 1; 63 dis[0] = 0; 64 while(!q.empty()) 65 { 66 int now = q.front(); q.pop(); 67 for(int i = 0; i < (int)G[now].size(); ++i) 68 { 69 Edge& e = edges[G[now][i]]; 70 if(!vis[e.to] && e.cap > e.flow) 71 { 72 vis[e.to] = 1; 73 dis[e.to] = dis[now] + 1; 74 q.push(e.to); 75 } 76 } 77 } 78 return vis[t]; 79 } 80 int cur[maxn << 2]; 81 int dfs(int now, int a) 82 { 83 if(now == t || !a) return a; 84 int flow = 0, f; 85 for(int& i = cur[now]; i < (int)G[now].size(); ++i) 86 { 87 Edge& e = edges[G[now][i]]; 88 if(dis[e.to] == dis[now] + 1 && (f = dfs(e.to, min(a, e.cap - e.flow))) > 0) 89 { 90 e.flow += f; 91 edges[G[now][i] ^ 1].flow -= f; 92 flow += f; a -= f; 93 if(!a) break; 94 } 95 } 96 return flow; 97 } 98 99 int maxflow() 100 { 101 int flow = 0; 102 while(bfs()) 103 { 104 Mem(cur); 105 flow += dfs(0, INF); 106 } 107 return flow; 108 } 109 110 //bool vis2[maxn]; 111 112 int main() 113 { 114 n1 = read(); n2 = read(); n3 = read(); 115 t = (n1 << 1)+ n2 + n3 + 1; //为了防止编号重复 116 /*按注释掉的写法会WA掉,因为如果一本书多次输入,那么这个书 117 就多次拆点,这一本书就可以使用多次了 */ 118 /* int m = read(); 119 while(m--) 120 { 121 int x = read(), y = read(); 122 addEdge(0, y + (n1 << 1)); 123 addEdge(y + (n1 << 1), x); 124 addEdge(x, x + n1); 125 vis2[x] = 1; 126 } 127 m = read(); 128 while(m--) 129 { 130 int x = read(), y = read(); 131 if(!vis2[x]) addEdge(x, x + n1), vis2[x] = 1; 132 addEdge(x + n1, y + (n1 << 1) + n2); 133 addEdge(y + (n1 << 1) + n2, t); 134 }*/ 135 int m = read(); 136 while(m--) 137 { 138 int x = read(), y = read(); 139 addEdge(y + (n1 << 1), x); 140 } 141 m = read(); 142 while(m--) 143 { 144 int x = read(), y = read(); 145 addEdge(x + n1, y + (n1 << 1) + n2); 146 } 147 for(int i = 1; i <= n1; ++i) addEdge(i, i + n1); 148 for(int i = 1; i <= n2; ++i) addEdge(0, i + (n1 << 1)); 149 for(int i = 1; i <= n3; ++i) addEdge(i + (n1 << 1) + n2, t); 150 write(maxflow()); enter; 151 }