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

推荐一下汪峰的《向阳花》,看着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 flow
	return 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(构图篇)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Your.Uninstaller!Pro.v7.3.2011.4最新安装版加上注册机,用注册机的话可以用自己喜欢的名字注册哦。如果帮女朋友装上并且用她的名字注册一下,你想一想效果会怎么样? (注册机、成人、反动等等的软件杀毒软件可能会报毒,但这个软件可以放心使用,保证没病毒。) ※※→→→→设置语言:View→Languages→Simplified Chinese←←←←※※ Your.Uninstaller!Pro.v7.3.2011.4 轻松并彻底地卸载掉一切不想要的Windows应用程序 Your Uninstaller! 是一个智能的卸载软件。你可以像使用回收站那样方便的管理你的应用程序。具备亲切、友好的用户界面。能干净、完整的卸载不需要的组件,它比 Windows 自带的卸载程序快 500% -1000%,并且准备一定的侦测 SPY 软件的能力。 如果你不满意Windows 自带的卸带工具,那么从现在开始,每周运行一次Your Uninstaller!,就可以让你的PC 在干净的环境下快速运行。 【软件特色】 您的软体移除工具,完全取代 Windows 内建的控制台「新增/移除程式」功能,并内建「启动」管理功能 软体介面清新整洁,可以用大图示、小图示、详细清单显示安装项目 以滑鼠点两下图示或使用滑鼠右键来移除软体 如果是最新安装的软体,其图示会以红色底图来强调显示,是一项体贴的功能设计 软体移除后会自动检查资料夹是否有残留档案,能将档案完全移除 【详细介绍】 Your Uninstaller! 是世界上第一个Windows 平台下智能的拖曳与卸载的卸载工具,它针对“Windows XP 的优化”,同样适合其他Windows 系列的操作系统。使用的时候,它的简单程序可以与Windows 下的垃圾桶相媲美。 程序运行之后,会有两个模式选择进入,“Basic”(基本模式)和“Pro”(专业模式)。在基本模式下,主程序窗口中,会把所有已经安装的程序列出来,一目了然。你可以根据需要,卸载不需要的安装程序。卸载包括了两种方式,“Uninstall”(卸载程序,保留注册表信息,方便再次安装使用)和“Clear”(彻底卸载并清除注册表信息);此外,程序还提供了对已安装程序的自动修复功能“Auto Fix”,假如某些程序运行不正常的话,可以先修复试试,不必急着卸载,然后又安装,可以省不少麻烦。 除了基本功能,假如你选择是的专业模式,功能当然更强劲了,扩展的功能包括清理开始菜单,临时文件夹,开机运行程序,IE浏览器以及备份还原功能。工作模式和卸载各种已安装的应用程序差不多,速度很快。 值得一提的是,除了主程序,桌面上你还会看见一个“1-Click Cleaning…”快捷图标,它是根据常用的清理路径做的默认设置扫描和清理功能,让你不需要每次都运行主程序再选择要清楚的对象那么麻烦,完全的用户化设计。 利用Your Uninstaller!,你可以轻松查看到各种工具软件的卸载信息,根据卸载信息的提示,可以知道哪些程序可以被彻底地卸载;除了具有卸载功能外,该程序还具有启动程序管理功能,利用该功能你可以自定义各种需要随计算机系统一起启动的应用程序;此外,为方便你使用鼠标右键,该工具还新增了IE上下文菜单管理功能,利用这个功能你能提高计算机的操作效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值