zoukankan      html  css  js  c++  java
  • 网络流 24 题汇总(LOJ 上只有 22 题???)

    太裸的我就不放代码了。。。(黑体字序号的题表示值得注意)

    1、搭配飞行员 [LOJ#6000]

    二分图最大匹配。

    2、太空飞行计划 [LOJ#6001]

    最小割常规套路、输出方案。(注:这题换行符要用

    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 2147483647
    
    int 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 ? ' ' : '
    ');
    			}
    		}
    		printf("%d
    ", 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 2147483647
    
    struct 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
    ", 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) 题很像,做法也一样,但是区间的权值变化了,不难处理。

  • 相关阅读:
    MySql之基础
    web篇---jQuery
    前端篇---CSS
    前端篇--HTML
    Python篇1.17---多进程
    Python篇1.16---socket编程
    Python篇1.15---模块与包
    Python番外篇---函数
    python番外篇---变量与数据类型
    【认真的完整版翻唱!】红莲之箭【あるふぁきゅん。】
  • 原文地址:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/7965713.html
Copyright © 2011-2022 走看看