P1401 城市

Posted init-神眷の樱花

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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;
}```

以上是关于P1401 城市的主要内容,如果未能解决你的问题,请参考以下文章

P1401 城市

Android - 导航抽屉 - 与动态菜单项重叠的片段

善待自己

Android:在片段中显示列表

为啥 saveStateInstance 在屏幕方向上不起作用?

二进制 XML 文件第 13 行:膨胀类片段时出错