其他分享
首页 > 其他分享> > P1401 城市

P1401 城市

作者:互联网

题意

\(n\)个点,\(m\) 条边的无向图,求出 \(t\) 条路径,使路径中最长的边最小。

题解

看到最大的最小应该能马上想到二分答案。

于是我们差的就是一个 check 函数了。

不正确暴力

从点 \(1\) 出发遍历,将遍历的边及其反边打上标记,直至有 \(t\) 条路径为止。

这么一看感觉仿佛还可以,但是仔细想想,这样做不仅复杂度不优,反而得到的答案还并不正确。

如下图:

可能在第一次遍历的时候,上面那条路径与 \(n\) 连接的那条边,已经被打上标记了。而第二条路径其实可以走下面到达 \(n\)的,但是却把上面的那条占了,就只能跑出 \(1\) 条路径,但其实是 \(2\) 条的。这种情况根据你存边的顺序是有可能出现的。

所以这样的暴力不行。

并查集

使用并查集来维护也挺妙的,这是其它题解的思路,我没有打过,但是据他说有一个点卡不过,不知道加了按秩合并和编译优化之后能不能卡过。

网络流

这道题其实就考了一个最大流。

为什么这道题能用网络流来做呢?

其实一条路径就可以看做是从源点流出去的一个单位流量,因为残量网络是一个 DAG 图,所以满足了一条边只能经过一个方向的限制条件。

而我们将每条边的容量设为 \(1\) ,则相当于限制了一条边只能被一条路径经过。感性理解一下相当于是有 \(t\) 列火车从源点出发,向汇点开去,因为火车很长,它到达了汇点之后,尾巴还没有离开源点,而每条边只有一条轨道,于是每列火车都把它经过的边上的轨道都占了,就不会出现有两列火车同时在一条边的情况,所以这样的话,每条边是不会被重复经过的。

而对于长度不满足二分限制的边不加入残量网络中即可。

代码

#include<cstdio>
#include<ctype.h>
#include<cstring>
#include<iostream>
#include<queue>

const int N = 205, M = 4e4 + 5;

int head[N], nex[M << 1], to[M << 1], w[M << 1], edge[M << 1], n, m, siz = 1, t, ans;

long long L = 0, R = 1e9 + 7, mid;

inline void add(int u, int v, int W) {
	nex[++siz] = head[u]; w[siz] = W;
	to[siz] = v; head[u] = siz;
	nex[++siz] = head[v]; w[siz] = W;
	to[siz] = u; head[v] = siz;
}

int d[N], now[N]; std:: queue < int > q;

bool bfs(int s) {
	memset(d, 0, sizeof(d));
	while(!q.empty()) q.pop();
	q.push(s); d[s] = 1; now[s] = head[s];
	while(!q.empty()) {
		int x = q.front(); q.pop();
		for(int i = head[x]; i; i = nex[i])
			if(edge[i] && !d[to[i]] && w[i] <= mid) {
				q.push(to[i]);
				now[to[i]] = head[to[i]];
				d[to[i]] = d[x] + 1;
				if(to[i] == n) return true;
			}
	}
	return false;
}

int dinic(int x, int flow) {
	if(x == n) return flow;
	int res = flow, k, i;
	for(i = now[x]; i && res; i = nex[i])
		if(edge[i] && d[to[i]] == d[x] + 1 && w[i] <= mid) {
			k = dinic(to[i], std::min(res, edge[i]));
			if(!k) d[to[i]] = 0;
			edge[i] -= k; edge[i ^ 1] += k;
			res -= k;
		}
	now[x] = i;
	return flow - res;
}

inline int read() {
	int x = 0, f = 1, c = getchar();
	for(; !isdigit(c); c = getchar())
		if(c == '-')
			f = -1;
	for(; isdigit(c); c = getchar())
		x = x * 10 + c - 48;
	return x * f;
}

int main() {
	n = read(), m = read(), t = read();
	for(int i = 1; i <= m; i++) {
		int u = read(), v = read(), W = read();
		add(u, v, W);
	}
	while(L <= R) {
		for(int i = 1; i <= siz; i++) edge[i] = 1;
		mid = (L + R) >> 1;
		int flow = 0, maxflow = 0;
		while(bfs(1))
			while(flow = dinic(1, 0x3f3f3f3f)) maxflow += flow;
		if(maxflow >= t)
			R = mid - 1, ans = mid;
		else 
			L = mid + 1;
	}
	printf("%d\n", ans);
	return 0;
}```

标签:路径,int,城市,P1401,源点,while,maxflow,include
来源: https://www.cnblogs.com/sjzyh/p/14876617.html