首页 > 其他分享 >费用流

费用流

时间:2024-11-13 19:19:58浏览次数:1  
标签:费用 int ll vis lst fe dis

暴力

int vis[N], lst[N];
ll dis[N], flow[N];
int SPFA() {
	rep(i, 1, n) dis[i] = INF;
	queue<int> q;
	q.push(s), dis[s] = 0, flow[s] = INF;
	while (!q.empty()) {
		int u = q.front();
		q.pop(), vis[u] = 0;
		for (int i = h[u]; i; i = e[i].n) {
			int v = e[i].t;
			if (e[i].w && dis[v] > dis[u] + e[i].c) {
				dis[v] = dis[u] + e[i].c;
				lst[v] = i, flow[v] = min(flow[u], e[i].w);
				if (!vis[v]) q.push(v), vis[v] = 1;
			}
		}
	}
	return dis[t] < INF;
}

ll F = 0, C = 0;
while (SPFA()) {
    ll ret = flow[t];
    F += ret, C += ret * dis[t];
    for (int u = t; u != s; u = e[lst[u] ^ 1].t)
        e[lst[u]].w -= ret, e[lst[u] ^ 1].w += ret;
}

原始对偶算法

英文:Primal-Dual

用处:用 Dijkstra 跑费用流,不支持负环

用 Johnson 里面的技巧,给每个点定义 \(h_u\),边 \((u,v,w)\) 的新边权为 \(w+h_u-h_v\)

为了让新边权 \(\ge0\),以源点为起始点跑最短路后 \(h_u\gets d_u\) 即可。

每次增广后,令 \(h_u\gets h_u+d_u\) 即可。

int vis[N];
ll H[N];
void SPFA() {
	memset(vis, 0, sizeof(vis));
	memset(H, 0x3f, sizeof(H));
	vis[s] = 1, H[s] = 0;
	queue<int> q;
	q.push(s), vis[s] = 1;
	while (!q.empty()) {
		int u = q.front();
		q.pop(), vis[u] = 0;
		for (int i = h[u]; i; i = e[i].n) {
			int v = e[i].t;
			if (e[i].w && H[v] > H[u] + e[i].c) {
				H[v] = H[u] + e[i].c;
				if (!vis[v]) q.push(v), vis[v] = 1;
			}
		}
	}
}
priority_queue<pair<ll, int>, vector<pair<ll, int>>, greater<pair<ll, int>>> q;
ll dis[N], flow[N];
int lst[N];
void Dijkstra() {
	memset(vis, 0, sizeof(vis));
	rep(i, 1, n) dis[i] = INF;
	dis[s] = 0, flow[s] = INF;
	q.push({0ll, s});
	while (!q.empty()) {
		int u = q.top().second;
		ll d = q.top().first;
		q.pop();
		if (d > dis[u]) continue;
		for (int i = h[u]; i; i = e[i].n) {
			int v = e[i].t;
			if (e[i].w && dis[v] > dis[u] + e[i].c) {
				dis[v] = dis[u] + e[i].c;
				lst[v] = i;
				flow[v] = min(flow[u], e[i].w);
				q.push({dis[v], v});
			}
		}
	}
}
ll F = 0, C = 0;
SPFA();
while (1) {
	Dijkstra();
	if (dis[t] == INF) break;
	ll ret = flow[t];
	F += ret, C += ret * dis[t];
	for (int u = t; u != s; u = e[lst[u] ^ 1].t)
		e[lst[u]].w -= ret, e[lst[u] ^ 1].w += ret;
	rep(i, 1, n) H[i] += dis[i];
}
cout << F << '\n' << C << '\n';

单纯形网络流

英文:Simplex

用处:在线求最小费用最大流(支持动态加边),支持负环,效率高

思路:在图中不断找负环,然后把他跑满。

他和线性规划中的单纯形法类似,以下是他的步骤:

  1. 加边 \(T\to S\),容量 \(+\inf\),费用 \(-\inf\)(而非 \(0\))
  2. 随便找到一棵生成树,满足每条树边都有剩余流量。
  3. 枚举每一条边,判断他加入后是否产生负环,如果不产生负环就跳过。
  4. 找到这条边两端点在树上的 LCA,并找出环上流量最小的边,存下整个环。
  5. 更新环上流量、答案费用,删去流量最小边,加入这条边,再调整边顺序,构成新的生成树。
  6. 回到第 3 步直到没有负环为止。
  7. 最大流是第 1 步所加边的流量,最小费用要加上最大流 \(\times\inf\)。

如何快速判负环?每个点维护到根路径和即可 \(O(1)\) 判。

正确性:由第 1 步保证了。

注意到他的步骤和“LCT维护最小生成树”有点类似。

  • 最小费用可行流:第 1 步加边的费用为 \(0\)、反边容量为 \(\inf\),第 7 步最小费用不变即可。
  • 最小费用最小流:事先 swap(s, t) 即可。
int nw, fa[N], fe[N], cir[N], tag[N];
ll sum[N];
void DFS(int u, int f, int c) {
	fa[u] = e[f].u, fe[u] = f, tag[u] = c;
	for (int i = h[u]; i; i = e[i].n)
		if (e[i].w && tag[e[i].v] != c) DFS(e[i].v, i, c);
}
ll Sum(int u) {
	if (tag[u] == nw) return sum[u];
	return tag[u] = nw, sum[u] = Sum(fa[u]) + e[fe[u]].c;
}
ll Push(int o) {
	int cnt = 0, del = 0, P = 2;
	ll C = 0, F = e[o].w;
	++nw;
	int p = e[o].u, lca = e[o].v;
	while (p) tag[p] = nw, p = fa[p];
	while (tag[lca] != nw) tag[lca] = nw, lca = fa[lca];
	for (int u = e[o].u; u != lca; u = fa[u]) {
		cir[++cnt] = fe[u];
		if (F > e[fe[u]].w) F = e[fe[u]].w, del = u, P = 0;
	}
	for (int u = e[o].v; u != lca; u = fa[u]) {
		cir[++cnt] = fe[u] ^ 1;
		if (F > e[fe[u] ^ 1].w) F = e[fe[u] ^ 1].w, del = u, P = 1;
	}
	cir[++cnt] = o;
	rep(i, 1, cnt) C += F * e[cir[i]].c, e[cir[i]].w -= F, e[cir[i] ^ 1].w += F;
	if (P == 2) return C;
	int u = e[o].u, v = e[o].v;
	if (P == 1) swap(u, v);
	int lste = o ^ P, lstu = v, tmp;
	while (lstu != del) {
		swap(fe[u], lste ^= 1), --tag[u];
		tmp = fa[u], fa[u] = lstu, lstu = u, u = tmp;
	}
	return C;
}
void Simplex() {
	eadd(t, s, INF, -INF);
	DFS(t, 0, ++nw);
	tag[t] = ++nw, fa[t] = 0;
	ll F = 0, C = 0;
	int ok = 1;
	while (ok) {
		ok = 0;
		rep(i, 2, tot)
			if (e[i].w && e[i].c + Sum(e[i].u) - Sum(e[i].v) < 0)
				C += Push(i), ok = 1;
	}
	F = e[tot].w;
	C += e[tot].w * INF;
	cout << F << '\n' << C << '\n';
}

标签:费用,int,ll,vis,lst,fe,dis
From: https://www.cnblogs.com/laijinyi/p/18544607

相关文章

  • OCP 19c 考试费用和备考建议
    OCP的英文全称是OracleCertifiedProfessional,它是Oracle数据库管理员的中级认证,代表着对Oracle数据库的操作达到了中级水平。OCP19c 考试费用:OCP19c考试需要考2科,每科考试费大概2000元,总考试费用是4000左右(不含补考费),除了考试费,还需要到Oracle指定的WDP合作机构......
  • 网络流&费用流&二分图
    NOIP也许考不到,但是可以拿来骗分也说不定(算法原理就算了,反正也不需要知道,只需要知道它在干什么并且会建图就行了。二分图就是左右两部点,同一部内的点无连边,可以考虑建二分图后网络流。持续放些题。一些基本理论和建模方式最小割=最大流最大权闭合子图切糕模型二......
  • 深度学习Python停车场智能车牌识别系统opencv流量费用时间AI源码
    随着智能交通技术的发展,停车场智能车牌识别系统逐渐成为现代停车管理的重要工具。该系统利用深度学习和计算机视觉技术,实现对车辆车牌的自动检测与识别,从而提高停车场的管理效率和用户体验。系统架构与功能模块车牌检测:系统首先利用目标检测算法(如YOLO或FasterR-CNN)对停车......
  • 每刻费用类型对接金蝶云星空的最佳实践
    每刻费用类型到金蝶费用项目的系统对接集成案例分享在企业财务管理中,数据的准确性和实时性至关重要。为了实现每刻费用类型数据与金蝶云星空费用项目的无缝对接,我们采用了轻易云数据集成平台,充分利用其高吞吐量的数据写入能力和实时监控功能,确保数据处理过程透明且高效。本次集......
  • 腾讯云tdsql认证的优势和考试费用
    腾讯云有很多产品,比如中间件、云计算、云架构、大数据、人工智能、机器学习等等的,数据库TDSQL是其中一种,对于使用腾讯云TDSQL的企业和员工来说,考腾讯云数据库TDSQL认证是比较有帮助的。一、腾讯云TDSQL数据库认证证书具有多方面的作用:1.对个人的作用:提升技术能力+增强职场......
  • Oracle认证证书的考试费用是多少
    近期有学员咨询时问到:他大学学的是it和计算机方面的课程,在投简历时经常会看到Oracle认证优先,所以来问问Oracle证书的事情。新接触数据库行业的毕业生或者转行的人可能不清楚Oracle认证的含金量,Oracle是非常有名的数据库产品,在db-ranking统计中,Oracle数据库一直霸占第一的位置,Orac......
  • 最小费用最大流
    终于把自己搞蒙了简而言之,最小费用最大流就是这样:图论中的一种理论与方法,研究网络上的一类最优化问题。很多系统中涉及流量问题,例如公路系统中车流量,网络中的数据信息流,供油管道的油流量等。我们可以将有向图进一步理解为“流网络”(flownetwork),并利用这样的抽象模型求解有关......
  • Leetcode 1584. 连接所有点的最小费用
    1.题目基本信息1.1.题目描述给你一个points数组,表示2D平面上的一些点,其中points[i]=[x_i,y_i]。连接点[x_i,y_i]和点[x_j,y_j]的费用为它们之间的曼哈顿距离:|x_i–x_j|+|y_i–y_j|,其中|val|表示val的绝对值。请你返回将所有点连接的最小总费用。只......
  • 选择2024年开发App的理由,费用分析与效益
    App开发费用受复杂度、团队、地理位置、平台等因素影响。低代码平台如ZohoCreator提供经济高效开发方案,降低费用并提升灵活性。2024年,企业需考虑这些因素制定长期规划。调查显示:企业估算应用开发费用时,常采用以下公式:总开发时间×开发每小时费率=应用开发费用。总开发时间包括......
  • [SDOI2011] 工作安排——费用流
    [SDOI2011]工作安排题目描述你的公司接到了一批订单。订单要求你的公司提供\(n\)类产品,产品被编号为\(1\simn\),其中第\(i\)类产品共需要\(C_i\)件。公司共有\(m\)名员工,员工被编号为\(1\simm\)员工能够制造的产品种类有所区别。一件产品必须完整地由一名员工制......