洛谷 P3000 [USACO10DEC] Cow Calisthenics G

思路

题目要求断若干条边后形成的连通块中,最大的直径最小,很明显的二分。关键就在于如何写 c h e c k check check 函数了。

可以用 d f s dfs dfs 来判断要断哪条边。

一、 d [ u ] d[u] d[u] 定义

设 d [ u ] d[u] d[u] 为从 u u u 出发到子树中【断开边后连通块的叶子节点】所经过的最多的节点数,包括 u u u 节点自己。

这句话可能比较难理解。设 v v v 为 u u u 的一个子节点,那么程序会先递归判断以 v v v 为根的子树中哪些边需要被断掉。那么再回朔到 u u u 节点时,子树 v v v 就算一个被删了一些边的连通块,那么子树 v v v 就会有一些新的叶子节点。这些新叶子节点到 u u u 会经过若干节点, d [ u ] d[u] d[u] 就代表【经过节点数最多的】那条路径上的【节点数】。

如此一来, d [ v ] d[v] d[v] 就表示从 v v v 出发到其子树叶子节点经过的最多节点数。同时,它还可以表示节点 u u u 走到【以 v v v 为根的子树的叶子节点】所经过的最多边数。

(本人在看其他题解时想了好一会才想明白,因此花了这么多文字来解释,太菜了)

二、断边条件

现在考虑什么情况断边。

假设 m a x d maxd maxd 是目前 所扫过的所有子节点 v v v 中 d [ ] d[] d[] 值最大的那个。

现在又扫完一个新节点 v ′ v' v′:若 m a x d + d [ v ′ ] > l i m i t maxd + d[v'] > limit maxd+d[v′]>limit,那就断边;否则用 d [ v ′ ] d[v'] d[v′] 更新 m a x d maxd maxd。

问题又来了,断边时有两条边可供断开(即: m a x d , d [ v ′ ] maxd, d[v'] maxd,d[v′] 分别代表的那一条),该断哪一条?

贪心的想,我们肯定要保留值较小的那一条。因为若保留大的,它未来可能会与其他更多的 d [ ] d[] d[] 相加超出限制,导致更多的断边,这样显然时更劣的。

代码

cpp 复制代码
#include <bits/stdc++.h>

using namespace std;

const int maxn = 1e5 + 7;

int n, m;
vector<int> e[maxn];

int d[maxn], cnt;
void dfs(int u, int f, int lim) {
	if (cnt > m) return ;
	int maxd = 0;
	for (int v : e[u]) {
		if (v == f) continue;
		dfs(v, u, lim);
		if (maxd + d[v] > lim)  // 超出限制, 断边, 并保留较小的那一条边 
		    ++cnt, maxd = min(maxd, d[v]);
		else maxd = max(maxd, d[v]);  // 没有超出限制, 更新 maxd 
	}
	d[u] = maxd + 1;
}
bool check(int x) {
	cnt = 0, dfs(1, 0, x);
	return cnt <= m;
}
int main() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i < n; ++i) {
		int u, v;
		scanf("%d%d", &u, &v);
		e[u].push_back(v);
		e[v].push_back(u);
	}
	
	// 二分最大直径的最小值 
    int l = 0, r = n - 1;
    int ans = 0;
	while (l <= r) {
		int mid = (l + r) >> 1;
		if (check(mid)) ans = mid, r = mid - 1;
		else l = mid + 1;
	}
	printf("%d\n", ans);
	return 0;
} 

若觉得此篇题解过于冗长,可以看看这篇

相关推荐
hnjzsyjyj1 天前
洛谷 P8749:[蓝桥杯 2021 省 B] 杨辉三角形 ← 组合数 + 二分
蓝桥杯·二分·杨辉三角·组合数
We་ct10 天前
LeetCode 373. 查找和最小的 K 对数字:题解+代码详解
前端·算法·leetcode·typescript·二分·
问好眼10 天前
《算法竞赛进阶指南》0x04 二分-1.最佳牛围栏
数据结构·c++·算法·二分·信息学奥赛
A923A11 天前
【洛谷刷题 | 第九天】
算法·二分·洛谷
We་ct13 天前
LeetCode 4. 寻找两个正序数组的中位数:二分优化思路详解
前端·数据结构·算法·leetcode·typescript·二分
We་ct14 天前
LeetCode 153. 旋转排序数组找最小值:二分最优思路
前端·算法·leetcode·typescript·二分·数组
We་ct15 天前
LeetCode 34. 在排序数组中查找元素的第一个和最后一个位置:二分查找实战
前端·算法·leetcode·typescript·二分
We་ct16 天前
LeetCode 33. 搜索旋转排序数组:O(log n)二分查找
前端·算法·leetcode·typescript·个人开发·二分·数组
We་ct17 天前
LeetCode 162. 寻找峰值:二分高效求解
前端·算法·leetcode·typescript·二分·暴力
yuhao_mice17 天前
# Array Game
二分