bzoj 3130 [Sdoi2013]费用流 二分答案+网络流

本文探讨了如何通过二分搜索求解最大流问题中流量最大的边的最小值,采用Dinic算法实现,并提供了详细的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题面

题目传送门

解法

  • 题目转化一下就可以发现要求最大流中流过的流量最大的那条边流量最小
  • 显然可以在实数域内二分答案 m i d mid mid,然后就变成所有边容量为 m i n ( c , m i d ) min(c,mid) min(c,mid)的情况下最大流是否不变
  • 直接 d i n i c dinic dinic似乎就可以了,精度要求不高

代码

#include <bits/stdc++.h>
#define double long double
#define eps 1e-6
#define N 110
using namespace std;
template <typename node> void chkmax(node &x, node y) {x = max(x, y);}
template <typename node> void chkmin(node &x, node y) {x = min(x, y);}
template <typename node> void read(node &x) {
	x = 0; int f = 1; char c = getchar();
	while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
	while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
struct Node {
	int x, y, c;
} a[N * 10];
struct Edge {
	int next, num; double c;
} e[N * N * 5];
int n, m, p, cnt, l[N], cur[N];
void add(int x, int y, double c) {
	e[++cnt] = (Edge) {e[x].next, y, c};
	e[x].next = cnt;
}
void Add(int x, int y, double c) {
	add(x, y, c), add(y, x, 0);
}
bool bfs() {
	for (int i = 2; i <= n; i++) l[i] = -1;
	queue <int> q; q.push(1);
	while (!q.empty()) {
		int x = q.front(); q.pop();
		for (int p = e[x].next; p; p = e[p].next) {
			int k = e[p].num; double c = e[p].c;
			if (c > eps && l[k] == -1)
				l[k] = l[x] + 1, q.push(k);
		}
	}
	return l[n] != -1;
}
double dfs(int x, double lim) {
	if (x == n) return lim;
	double used = 0;
	for (int p = cur[x]; p; p = e[p].next) {
		int k = e[p].num; double c = e[p].c;
		if (c > eps && l[k] == l[x] + 1) {
			double w = dfs(k, min(lim - used, c));
			e[p].c -= w, e[p ^ 1].c += w, used += w;
			if (e[p].c > eps) cur[x] = p;
			if (lim - used < eps) return lim;
		}
	}
	if (used < eps) l[x] = -1; return used;
}
double dinic() {
	double ret = 0;
	while (bfs()) {
		for (int i = 1; i <= n; i++) cur[i] = e[i].next;
		ret += dfs(1, INT_MAX);
	}
	return ret;
}
bool check(double mid, double tmp) {
	cnt = n + (n % 2 == 0);
	for (int i = 1; i <= n; i++) e[i].next = 0;
	for (int i = 1; i <= m; i++) {
		int x = a[i].x, y = a[i].y; double c = a[i].c;
		if (mid > c) Add(x, y, c); else Add(x, y, mid);
	}
	return tmp - dinic() < eps;
}
int main() {
	read(n), read(m), read(p); cnt = n + (n % 2 == 0);
	for (int i = 1; i <= m; i++) {
		read(a[i].x), read(a[i].y), read(a[i].c);
		Add(a[i].x, a[i].y, a[i].c);
	}
	double tmp = dinic(); cout << tmp << "\n";
	double l = 0, r = tmp;
	while (l + eps < r) {
		double mid = (l + r) / 2;
		if (check(mid, tmp)) r = mid;
			else l = mid;
	}
	cout << fixed << setprecision(6) << r * p << "\n";
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值