网络流 24 题汇总(LOJ 上只有 22 题???)
太裸的我就不放代碼了。。。(黑體字序號的題表示值得注意)
1、搭配飛行員 [LOJ#6000]
二分圖最大匹配。
2、太空飛行計劃 [LOJ#6001]
最小割常規套路、輸出方案。(注:這題換行符要用 \r)
3、最小路徑覆蓋 [LOJ#6002]
網上大多數題解都是二分圖相關的,但這題有一個更直觀的做法。
我們限制每個點的流量上下界都為 \(1\),從源點向每個點的“入點”連容量為 \(1\) 的邊,從每個點的“出點”向匯點連容量為 \(1\) 的邊,然后跑最小流。
最后輸出方案暴力 dfs 即可。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cctype> #include <algorithm> using namespace std; #define rep(i, s, t) for(int i = (s); i <= (t); i++) #define dwn(i, s, t) for(int i = (s); i >= (t); i--)const int BufferSize = 1 << 16; char buffer[BufferSize], *Head, *Tail; inline char Getchar() {if(Head == Tail) {int l = fread(buffer, 1, BufferSize, stdin);Tail = (Head = buffer) + l;}return *Head++; } int read() {int x = 0, f = 1; char c = Getchar();while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); }while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); }return x * f; }#define maxn 410 #define maxm 136010 #define oo 2147483647int at[maxn]; int CntP; struct Point {int id;Point(): id(0) {}int p() { return id ? id : id = ++CntP; } } In[maxn], Out[maxn], S, T, SS, TT;struct Edge {int from, to, flow;Edge() {}Edge(int _1, int _2, int _3): from(_1), to(_2), flow(_3) {} }; struct Dinic {int n, m, s, t, head[maxn], nxt[maxm];Edge es[maxm];int Q[maxn], hd, tl, vis[maxn];int cur[maxn];bool CanPass, etag[maxm];int cntp, path[maxn];void init() {m = 0; memset(head, -1, sizeof(head));return ;}void setn(int _) {n = _;return ;}void AddEdge(int a, int b, int c) {es[m] = Edge(a, b, c); nxt[m] = head[a]; head[a] = m++;es[m] = Edge(b, a, 0); nxt[m] = head[b]; head[b] = m++;return ;}bool BFS() {memset(vis, 0, sizeof(vis));vis[t] = 1;hd = tl = 0; Q[++tl] = t;while(hd < tl) {int u = Q[++hd];for(int i = head[u]; i != -1; i = nxt[i]) {Edge& e = es[i^1];if(!CanPass && etag[i]) continue;if(!vis[e.from] && e.flow) {vis[e.from] = vis[u] + 1;Q[++tl] = e.from;}}}return vis[s] > 1;}int DFS(int u, int a) {if(u == t || !a) return a;int flow = 0, f;for(int& i = cur[u]; i != -1; i = nxt[i]) {Edge& e = es[i];if(!CanPass && etag[i]) continue;if(vis[e.to] == vis[u] - 1 && (f = DFS(e.to, min(a, e.flow)))) {flow += f; a -= f;e.flow -= f; es[i^1].flow += f;if(!a) return flow;}}return flow;}int MaxFlow(int _s, int _t, bool canpass) {s = _s; t = _t; CanPass = canpass;int flow = 0;while(BFS()) {rep(i, 1, n) cur[i] = head[i];flow += DFS(s, oo);}return flow;}void getpath(int u) {path[++cntp] = at[u];for(int i = head[u]; i != -1; i = nxt[i]) {Edge& e = es[i];if(e.flow < oo && at[e.to]) getpath(Out[at[e.to]].p());}return ;}void print(int s) {int cnt = 0;for(int i = head[s]; i != -1; i = nxt[i]) {Edge& e = es[i];if(!e.flow && e.to != t) {cnt++;cntp = 0; getpath(Out[at[e.to]].p());rep(i, 1, cntp) printf("%d%c", path[i], i < cntp ? ' ' : '\n');}}printf("%d\n", cnt);return ;} } sol;int main() {int n = read(), m = read();sol.init();rep(i, 1, n)sol.AddEdge(SS.p(), Out[i].p(), 1), sol.AddEdge(In[i].p(), TT.p(), 1),sol.AddEdge(S.p(), In[i].p(), 1), sol.AddEdge(Out[i].p(), T.p(), 1),at[In[i].p()] = at[Out[i].p()] = i;sol.etag[sol.m] = sol.etag[sol.m^1] = 1;sol.AddEdge(T.p(), S.p(), oo);rep(i, 1, m) {int a = read(), b = read();sol.AddEdge(Out[a].p(), In[b].p(), oo);}sol.setn(CntP);sol.MaxFlow(SS.p(), TT.p(), 1);sol.MaxFlow(T.p(), S.p(), 0);sol.print(S.p());return 0; }4、魔術球 [LOJ#6003]
二分答案(答案不超過 \(2000\)),然后和上一題基本一樣。(注:這題不能用 fread())
5、圓桌聚餐 [LOJ#6004]
二分圖匹配、輸出方案。
6、最長遞增子序列 [LOJ#6005]
先 dp 出 \(f_i\) 表示以第 \(i\) 個數結尾的最長不降子序列長度,然后對于 \(?(i, j)\) 滿足 \(i < j, f_i + 1 = f_j, A_i \le A_j\),在第 \(i\) 個數和第 \(j\) 個數之間連邊,然后點限制流量為 \(1\)(第三問把第一個和第 \(n\) 個節點的流量限制改成無窮),跑最大流。
7、試題庫 [LOJ#6006]
同第 \(5\) 題。注意這題有可能有某個類型須要 \(0\) 道題。
8、方格取數 [LOJ#6007]
將棋盤黑白染色后跑最小割。
9、餐巾計劃 [LOJ#6008]
有下界的最小費用可行流,詳見這里(網上許多做法似乎沒這個直觀,當然他們是做了優化)。
10、數字梯形 [LOJ#6010]
最小費用最大流裸題。
11、運輸問題 [LOJ#6011]
最小費用最大流裸題。
12、分配問題 [LOJ#6012]
同上。
13、負載平衡 [LOJ#6013]
小于平均值的倉庫向匯點連容量為平均值減倉庫存量的邊,源點向大于平均值的倉庫連容量為庫存減平均值的邊(費用均為 \(0\)),然后相鄰倉庫連容量無窮費用為 \(1\) 的雙向邊。最后跑最小費用最大流。
14、最長 k 可重區間集 [LOJ#6014]
這個題有點厲害啊,想了半天。。。
還是有一個直接的思路。把坐標離散化后,最后的收益相當于每段小區間的長度乘它被覆蓋的次數的總和。對于一個給出的開區間,我們可以用“環”的性質保證它出了 \(1\),最后能夠回來 \(1\)。就是說我們將所有點從左到右依次連邊(容量為 \(k\),費用為負的區間長度,這里的區間長度就是它和下一個點的距離),然后對于一個區間 \((l, r)\),連一條從點 \(r\) 到點 \(l\),容量為 \(1\),費用為 \(0\) 的邊,最后跑最小費用可行流。(注意這個圖是有負環的,我們可以先強行流滿所有的負邊,然后用類似有下界可行流的辦法進行調整)
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cctype> #include <algorithm> using namespace std; #define rep(i, s, t) for(int i = (s); i <= (t); i++) #define dwn(i, s, t) for(int i = (s); i >= (t); i--)int read() {int x = 0, f = 1; char c = getchar();while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }return x * f; }#define maxn 1510 #define maxm 3014 #define oo 2147483647struct Edge {int from, to, flow, cost;Edge() {}Edge(int _1, int _2, int _3, int _4): from(_1), to(_2), flow(_3), cost(_4) {} }; struct ZKW {int n, m, s, t, cost, ans, head[maxn], nxt[maxm];Edge es[maxm];bool inq[maxn];int d[maxn], Q[maxn*10], hd, tl;bool vis[maxn];void init() {m = 0; memset(head, -1, sizeof(head));return ;}void setn(int _) {n = _;return ;}void AddEdge(int a, int b, int c, int d) {es[m] = Edge(a, b, c, d); nxt[m] = head[a]; head[a] = m++;es[m] = Edge(b, a, 0, -d); nxt[m] = head[b]; head[b] = m++;return ;}bool BFS() {rep(i, 1, n) d[i] = oo;d[t] = 0;hd = tl = 0; Q[++tl] = t; inq[t] = 1;while(hd < tl) {int u = Q[++hd]; inq[u] = 0;for(int i = head[u]; i != -1; i = nxt[i]) {Edge& e = es[i^1];if(d[e.from] > d[u] + e.cost && e.flow) {d[e.from] = d[u] + e.cost;if(!inq[e.from]) inq[e.from] = 1, Q[++tl] = e.from;}}}if(d[s] == oo) return 0;cost = d[s];return 1;}int DFS(int u, int a) {if(u == t || !a) return ans += cost * a, a;if(vis[u]) return 0;vis[u] = 1;int flow = 0, f;for(int i = head[u]; i != -1; i = nxt[i]) {Edge& e = es[i];if(d[e.to] == d[u] - e.cost && (f = DFS(e.to, min(a, e.flow)))) {flow += f; a -= f;e.flow -= f; es[i^1].flow += f;if(!a) return flow;}}return flow;}int MaxFlow(int _s, int _t) {s = _s; t = _t;int flow = 0, f;while(BFS())do {memset(vis, 0, sizeof(vis));f = DFS(s, oo);flow += f;} while(f);return flow;} } sol;#define pii pair <int, int> #define x first #define y second #define mp(x, y) make_pair(x, y)pair <int, int> line[maxn]; int num[maxn], cntn;int main() {int n = read(), k = read();rep(i, 1, n) {int l = read(), r = read();if(l > r) swap(l, r);line[i] = mp(l, r);num[++cntn] = l; num[++cntn] = r;}sort(num + 1, num + cntn + 1);cntn = unique(num + 1, num + cntn + 1) - num - 1;rep(i, 1, n)line[i].x = lower_bound(num + 1, num + cntn + 1, line[i].x) - num,line[i].y = lower_bound(num + 1, num + cntn + 1, line[i].y) - num;int S = cntn + n + 1, T = S + 1, sum = (num[cntn] - num[1]) * k;sol.init(); sol.setn(T);sol.AddEdge(S, cntn, k, 0); sol.AddEdge(1, T, k, 0);rep(i, 2, cntn) sol.AddEdge(i, i - 1, k, num[i] - num[i-1]);rep(i, 1, n) sol.AddEdge(line[i].y, line[i].x, 1, 0);sol.MaxFlow(S, T);printf("%d\n", sum - sol.ans);return 0; }15、星際轉移 [LOJ#6015]
這題其實應該給出一個條件:天數不會超過 \(30\)。那就枚舉答案然后分層建圖網絡流檢驗。
16、孤島營救問題 [LOJ#6121]
這題網絡流怎么做?!
bfs 做法顯然。令狀態 \((x, y, s)\) 表示身處 \((x, y)\) 格子,當前有的鑰匙集合為 \(s\),然后就搜就行了。
17、航空路線問題 [LOJ#6122]
\(2\) 到 \(n - 1\) 號城市限流為 \(1\),\(1\) 和 \(n\) 號城市限流為 \(2\),所有城市費用為 \(-1\),跑最小費用最大流,若最大流為 \(2\) 則有界,沿著流輸出。
18、汽車加油行駛問題 [LOJ#6223]
這題也許是告訴我們費用流可以用來跑最短路吧。。。
令狀態 \((x, y, k)\) 表示在坐標 \((x, y)\) 處,當前有 \(k\) 的油量。跑最短路即可。
19、深海機器人問題 [LOJ#6224]
最小費用最大流裸題。
20、火星探險問題 [LOJ#6225]
最小費用最大流裸題 + 輸出方案。
21、騎士共存問題 [LOJ#6226]
同樣是棋盤染色 + 最小割。
22、最長k可重線段集問題 [LOJ#6227]
這題和第 \(14\) 題很像,做法也一樣,但是區間的權值變化了,不難處理。
轉載于:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/7965713.html
總結
以上是生活随笔為你收集整理的网络流 24 题汇总(LOJ 上只有 22 题???)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: PAT——1022. D进制的A+B
- 下一篇: 表单令牌阻止数据重复提交