2277. 秘密挤奶机(最大流,最大流判定,二分)

文章介绍了在一个农场地图上,如何通过最大流算法和二分查找策略,寻找在限制条件下约翰从牛棚到挤奶机的最短路径中,最长单边道路的最小长度。

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

活动 - AcWing

农夫约翰正在制造一台新的挤奶机,并希望对这件事进行严格保密。

他将挤奶机藏在了农场的深处,使他能够在不被发现的情况下,进行这项任务。

在机器制造的过程中,他要在牛棚和挤奶机之间一共进行 T 次往返。

他有一个秘密通道只能在返程时使用。

农场由 N 个地标(编号 1∼N)组成,这些地标由 P 条双向道路(编号 1∼P)连接。

每条道路的长度为正,且不超过 106106。

多条道路可能连接同一对地标。

为了尽可能的减少自己被发现的可能性,农场中的任何一条道路都最多只能使用一次,并且他应该尝试使用尽可能短的道路。

帮助约翰从牛棚(地标 1)到达秘密挤奶机(地标 N)总共 T 次。

找到他必须使用的最长的单个道路的最小可能长度。

请注意,目标是最小化所使用的最长道路的长度,而不是最小化所有被使用道路的长度之和。

保证约翰可以在不走重复道路的情况下完成 T 次行程。

输入格式

第一行包含三个整数 N,P,T。

接下来 P 行,每行包含三个整数 Ai,Bi,Li,表示地标 Ai 和 Bi 之间存在一条长度为 Li 的道路。

输出格式

输出一个整数,表示约翰必须使用的最长的单个道路的最小可能长度。

数据范围

1≤T≤200
2≤N≤200
1≤P≤40000
1≤Ai,Bi≤N
1≤Li≤106

输入样例:
7 9 2
1 2 2
2 3 5
3 7 5
1 4 1
4 3 1
4 5 7
5 7 1
1 6 3
6 7 3
输出样例:
5
样例解释

约翰可以选择以下两条路径:1−2−3−71−2−3−7 和 1−6−71−6−7。

两条路径中包含的所有道路长度均不超过 55。

约翰无法在仅使用长度小于 55 的道路的情况下,两次从 11 到 77。

解析: 

题目要求:求起点到终点的不相交路径中边长最长的边最小值。

二分:由于题目求的时最大值最小,因此偶先考虑二分。

二段性判定:二分边长,如果当前的边长 mid 时最终答案,及在不大于 mid 的边长的边中能找到 T 条路经,那么在大于 mid 的集合中一定包含正确答案的边的集合;又因为 mid 是答案,因此答案的边的集合一定含边长为 mid 的边,小于 mid 的边的集合一定不包含边长为 mid 的边

可以使用最大流算法判断二分结果,及判断是否有 T 条不相交路径。

 正反边均有流量:

由于最大流算法只能处理有向图,因此我们需要将无向图转换成有向图。每一条无向边等价于两条有向边,且流量均为 1。同时我们还可以将这两条有向边与残留网络中的反向边合并,正确性我们可以通过(1)流量守恒,(2) 容量限制,来判断。

可行流与不相交路径的关系:(问题转化的正确性判断)

可行流 ——>不相交路径:由于每条边的容量为 1,且算法实现是的容量最小单位就是 1,因此每条边只能使用一次,且类似欧拉路径的证明方法,每个出边一定对应每条路径。

不相交路径 ——>可行流:对于每条边,使用过就等价于流过 1 个单位的流量。

#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<math.h>
#include<map>
#include<sstream>
#include<deque>
#include<unordered_map>
#include<unordered_set>
#include<bitset>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
const int N = 2e2 + 10, M = 8e4 + 5, INF = 0x3f3f3f3f;
int n, m, k, S, T;
int h[N], e[M], f[M], w[M], ne[M], idx;
int q[N], d[N], cur[N];

void add(int a, int b, int c) {
	e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
	e[idx] = a, w[idx] = c, ne[idx] = h[b], h[b] = idx++;
}

bool bfs() {
	int hh = 0, tt = 0;
	memset(d, -1, sizeof d);
	q[0] = S, d[S] = 0, cur[S] = h[S];
	while (hh <= tt) {
		int t = q[hh++];
		for (int i = h[t]; i != -1; i = ne[i]) {
			int j = e[i];
			if (d[j] == -1 && f[i]) {
				d[j] = d[t] + 1;
				cur[j] = h[j];
				if (j == T)return 1;
				q[++tt] = j;
			}
		}
	}
	return 0;
}

int find(int u, int limit) {
	if (u == T)return limit;
	int flow = 0;
	for (int i = cur[u]; i != -1 && flow < limit; i = ne[i]) {
		int j = e[i];
		cur[u] = i;
		if (d[j] == d[u] + 1 && f[i]) {
			int t = find(j, min(f[i], limit - flow));
			if (!t)d[j] = -1;
			f[i] -= t, f[i ^ 1] += t, flow += t;
		}
	}
	return flow;
}

int dinic() {
	int ret = 0, flow;
	while (bfs())while (flow = find(S, INF))ret += flow;
	return ret;
}

bool check(int mid) {
	for (int i = 0; i < idx; i++) {
		if (w[i] > mid)f[i] = 0;
		else f[i] = 1;
	}
	int t = dinic();
	//cout << "________________________________  " << t <<" "<<k << endl;
	return  t>= k;
}

int main() {
	cin >> n >> m >> k;
	memset(h, -1, sizeof h);
	S = 1, T = n;
	for (int i = 1,a,b,c; i <= m; i++) {
		scanf("%d%d%d", &a, &b, &c);
		add(a, b, c);
	}
	int l = 0, r = 1e8,mid;
	while (l < r) {
		mid = (l + r) >> 1;
		//cout << "JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ   "<<mid << endl;
		if (check(mid))r = mid;
		else l = mid + 1;
	}
	cout << r << endl;
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值