【说说网络流的那些事】网络流基础知识Ver0.1(算法篇)

来源:互联网 发布:流星搜剑录激活码淘宝 编辑:程序博客网 时间:2024/06/05 04:52

推荐一下汪峰的《向阳花》,看着100期战报,听着,就爱上了。



Statement:

我是弱X...讲的东西很弱...杂鱼求放过啊...


1.What‘s Network Flow

网络流就是在一个网络上流嘛..

相当于一个限制水流向的水管系统。问从源点到汇点的最大水流速。


2.最大流

算法:FF、EK、HLPP、ISAP、Dinic

他们是什么?

FF不知道。从EK说起。

EK:基础的最大流算法,每次Bfs寻找最短路进行增广,这时候的增广和匈牙利算法的增广不同,找出一条残余路径就可以了。

HLPP:使用了距离标号。理论复杂度最优。O(n^2 * sqrt(m)), 但常常跑不过sap,代码(据说)冗长。与增广路算法思想不怎么相同。

从EK到Dinic:引入HLPP的距离标号。每次只增广两端点距离标号差为一的边。

从Dinic到isap:

一群人闲着无聊,觉得dinic太慢了,就加上了重标号技术,使得算法无须在每次增广后再进行BFS每个顶点进行距离标号,

这种主动标号技术使得修正后算法的速度有了不少提高。但这点提高是不足称道的。

人们又发现当某个标号的值没有对应的顶点后,即增广路被截断了,于是算法便可以提前结束,这种启发式的优化称为Gap优化。

最后人们结合了连续增广,分层图,多路增广,Gap优化,主动标号等穷凶极恶的优化,更甚者在此之上狂搞个手动递归,

于是产生了增广路算法的高效算法–ISAP算法。所以推荐isap,当然dinic已经足够应付大多数考试。

模板:

sap(非递归)

(能再漂亮点么【喂..自重....】)(事实证明不但好看还有用..强虐标程)

(比牛牛的Dinic还短 http://blog.csdn.net/jerrydung/article/details/8149652)


#include<cstdio>#include<cstring>#define adde(a, b, c) ({*(++eptr) = (edge){b, c, h[a], eptr + 1}, h[a] = eptr;\*(++eptr) = (edge){a, 0, h[b], eptr - 1}, h[b] = eptr;})#define oo 0x3f3f3f3f#define ot "%d"#define maxe 610005#define maxn 310005#define ms(a, b) memset((a), (b), sizeof (a))using namespace std;struct edge{int t, v;edge *nt, *rev;}eg[maxe], *ed[maxe], *h[maxe], *pre[maxn], *now[maxn], *eptr;int n, m, dis[maxn], gap[maxn], que[maxn], head, tail, s, t;void bfs(){dis[t] = 0, gap[0] = 1;head = 0, tail = 1, que[1] = t;while(head != tail){int p = que[++head];for (edge *e = h[p]; e; e = e -> nt)if (!e -> v && (dis[e -> t] > dis[p] + 1))++gap[dis[que[++tail] = e -> t] = dis[p] + 1];}}int sap(int num){int u = s, v, w, flow, ans = 0;ms(dis, 0x3f); ms(gap, 0); bfs();while (dis[s] < n){for (bool flag = 1; flag;){flag = 0; if (!now[u]) now[u] = h[u];for (edge *e = now[u]; e && !flag; e = e -> nt)if (e -> v && dis[v = e -> t] == dis[u] - 1){now[u] = e; pre[u = v] = e; flag = 1;if (u == t){flow = oo;for (int i = u; i != s; i = pre[i] -> rev -> t)if (flow > pre[i] -> v) flow = pre[i] -> v;for (edge *d; u != s; u = d -> rev -> t)d = pre[u], d -> v -= flow, d -> rev -> v += flow;ans += flow; while (now[u] -> v) u = now[u] -> t;}}}w = n; if (!(--gap[dis[u]])) break;for (edge *e = now[u] = h[u]; e; e = e -> nt)if (e -> v) if (w > dis[e -> t]) w = dis[e -> t];++gap[dis[u] = w + 1]; if (u != s) u = pre[u] -> rev -> t;}return ans;}int main(){freopen("maxflow.in","r",stdin);freopen("maxflow.out","w",stdout);scanf(ot ot, &n, &m);ms(h, 0); int a = 0, b = 0, c = 0; eptr = eg;for (int i = 1; i <= m; ++i) scanf(ot ot ot, &a, &b, &c), adde(a, b, c);s = 1; t = n;printf(ot, sap(n));return 0;}



另附最大流强数据_YSQ : http://pan.baidu.com/share/link?shareid=114983&uk=3491581918

3. 费用流

最大流的基础上费用最小。

算法:zkw,暴力spfa,消圈,Primal-Duel(听起来很厉害?其实就是spfa多路增广)推荐二分图/小边权图 zkw, 其他PD

详见http://www.artofproblemsolving.com/blog/54262

Primal-Duel模板(经典费用流napkin):(zkw的先欠着...)

#include<iostream>#include<cstdio> #include<cstdlib>#include<cstring>#include<cmath>#include<algorithm>#include<climits>#define ot "%d"#define kg " "#define kh "\n"#define otl "%I64d"#define max(a, b) ({int _ = (a), __ = (b); _ > __ ? _ : __;})#define min(a, b) ({int _ = (a), __ = (b); _ < __ ? _ : __;})#define swap(a, b) ({int _ = (a); (a) = (b); (b) = _;})#define maxn 2002#define maxm 120002#define oo 0x3f3f3f3f#define ms(a, b) memset(a, b, sizeof a)#define adde(a, b, c, d)  {*(++eptr) = (edge){b, c,  d, eptr + 1, h[a]}, h[a] = eptr;\*(++eptr) = (edge){a, 0, -d, eptr - 1, h[b]}, h[b] = eptr;}struct edge{int t, v, c;edge *rev, *nt;}eg[maxm], *ed[maxm], *eptr, *h[maxn];using namespace std;int n, p, m, a, fa, b, fb, s, t, r, cost, tot;int head, tail, que[maxn * 5 + 5], dis[maxn];bool vis[maxn];void init(){freopen("napk.in", "r", stdin);freopen("napk.out", "w", stdout); eptr = eg;scanf(ot ot ot ot ot ot, &n, &p, &a, &fa, &b, &fb);ms(h, 0); s = (n << 1) + 1; t = (n << 1) + 2;for (int i = 1; i <= n; ++i){scanf(ot, &r);adde(s    ,     i,  r, 0);adde(s    , i + n, oo, p);adde(i + n,     t,  r, 0);}for (int i = n - 1; i; --i) adde(i,     i + 1, oo,  0);for (int i = n - a; i; --i) adde(i, n + i + a, oo, fa);for (int i = n - b; i; --i) adde(i, n + i + b, oo, fb);}bool labelit(){ms(dis, oo);dis[t] = head = tail = 0; que[++tail] = t;while (head != tail){int p = que[++head], q;for (edge *e = h[p]; e; e = e -> nt) if (e -> rev -> v && (q = dis[p] - e -> c) < dis[e -> t])dis[que[++tail] = e -> t] = q;//Special attention paid (Via DRJ)}if (dis[s] >= oo) return 0;for (int i = 1; i <= t; ++i)for (edge *e = h[i]; e; e = e -> nt)e -> c += dis[e -> t] - dis[i];return tot += dis[s], 1;}int work(int k, int flow){if (k == t) return cost += flow * tot, flow;vis[k] = 1; int tmp = flow;for (edge *e = h[k]; e; e = e -> nt)if (e -> v && !e -> c && !vis[e -> t]){int p = work(e -> t, tmp < e -> v ? tmp : e -> v);tmp -= p; e -> v -= p; e -> rev -> v += p;if (!tmp) return flow;}return flow - tmp;}int mcf(){tot = cost = 0;while (labelit()){do ms(vis, 0);while(work(s, oo));}return cost;}int main(){init();printf(ot, mcf()); //Minimum-cost flowreturn 0;}



4.上下界网络流

对于一些边,限定其流量上界和下界。

解法:详见《一种简易的方法求解流量有上下界的网络 》 还有 《最大流在信息学竞赛中应用的一个模型

模板:懒得写了...


另外把基哥的一句话小结各种网络流蒯过来:(原文:http://blog.csdn.net/pouy94/article/details/6628521)

(我就不说qw半年前就在看了...)

最大流:DINIC or SAP

最小费用最大流:SPFA+增广(费用的值较离散) or ZKW(费用的值集中)

有源汇的上下界最大流:新建s', t',用(i, j, l, r)表示i到j有一条下界为l上界为r的边,将每条这样的边拆成(s', j, 0, l), (i, t', 0, l), (i, j, 0, r-l),加入边(t, s, 0, max)再从s‘到t'求最大流,再去掉(t, s, 0, max)这条边,从s到t求最大流

有源汇的上下界最小可行流:基本同上,将最后一步改成从t到s反求一遍最大流来退流(注意可能大于可行流);也可以二分(t, s, 0, max)这条边的容量(比较慢...)

有源汇的上下界最小费用可行流:拆边方法同上,从s'向t'求一遍最小费用最大流
有源汇的上下界最小费用最大流:基本同上,还要再s向t求一遍最小费用最大流(*这个是自己YY的,可能不对,求指教)
无源汇的最大流(无向图全局最小割):Stoer-Wagner算法
无源汇的所有点对间最大流:分治,在当前点集内随便选两个点求最小割,用这个割更新一遍所有跨在两边的点对(不一定只是当前点集内的点),
再将自己的点集割成两部分,递归做

无源汇的上下界可行流:拆边,直接从s'到t'跑一遍最大流
无源汇的上下界最小费用可行流:拆边,直接从s'到t'跑一遍最小费用最大流

平面图最小割转最短路:将平面区域当成点,两个点之间的边权为原来这两个平面区域之间的边的容量,补上一条汇到源的正无穷边之后,求这条正无穷边的一边到另一边的最短路


基本上就是这样了。

另外新坑预定:Ver0.2(构图篇)

原创粉丝点击