树形DP树的中心

Posted Vincent_0000

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了树形DP树的中心相关的知识,希望对你有一定的参考价值。

题目来源

点我进入ACwing提交题目

题目描述

给定一棵树,树中包含 n 个结点(编号1~n)和 n−1 条无向边,每条边都有一个权值。

请你在树中找到一个点,使得该点到树中其他结点的最远距离最近。

输入格式
第一行包含整数 n。

接下来 n−1 行,每行包含三个整数 ai,bi,ci,表示点 ai 和 bi 之间存在一条权值为 ci 的边。

输出格式
输出一个整数,表示所求点到树中其他结点的最远距离。

数据范围
1≤n≤10000,
1≤ai,bi≤n,
1≤ci≤105
输入样例:
5
2 1 1
3 2 1
4 3 1
5 1 1
输出样例:
2

题目思路

  • 分析题意

题目简单明了,就是在树中找到一个点,使得该点到树中其他结点的最远距离最近。

  • 分析题型

暴力求解很容易超时,需要使用备忘录记忆一下结果, 又是求最值而且是在树上进行操作,那么尝试使用树形DP。

  • DP分析

首先计算出每个点到其他点的最远距离。
到其他点有两个大方向,一个是向上走,一个是向下走。(但是不是每一个点都具有这个性质!)
先考虑一下向下走,因为比较容易想,使用dfs,我们可以直接知道没个点向下走的最大距离是多少。
再考虑向上走:考虑向上走的话,我们就会把关注点放在父节点上,父节点和该节点一样,也是具有这些性质的,我们要从父节点上面得到的就是除了不走该节点以外的路线中的最大值。那么他也有三种可能 :①该最大值在上方(取上方最大值即可),②该最大值在下方却和之前那个节点路线重合了(那么就需要取下方第二大的值), ③该最大值在下方,没有与之前那个节点重合(取下方最大值)。
通过分析这三种情况发现,我们在第一小步向下走的时候还需要顺便求一下第二大的值。

然后再比较往上走和往下走取最最大值,再和其他点比较求最小值即可。

AC代码

#include<bits/stdc++.h> 
using namespace std;

#define _for(i, a, b) for (int i = (a); i < (b); ++i) 
#define _rep(i, a, b) for (int i = (a); i <= (b); ++i)
#define For(i, a, b) for (int i = (a); i >= (b); --i)
#define debug(a) cout << #a << " = " << a << ENDL
#define ENDL "\\n"
#define x first 
#define y second 
typedef long long ll;
typedef pair<int, int> pii;
typedef vector<int> vi;

const int N = 10000 + 5, INF = 0x3f3f3f3f;
int h[N], ne[2 * N], w[2 * N], e[2 * N], idx, d1[N], d2[N], up[N], p[N];
bool st[N];

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

int dfs_d(int u, int fa) {
	for (int i = h[u]; ~i; i = ne[i]) {
		int j = e[i];
		if (j == fa) continue;

		int d = dfs_d(j, u) + w[i];
		if (d >= d1[u]) {
			d2[u] = d1[u];
			d1[u] = d;
			p[u] = j;
		}
		else if (d > d2[u]) d2[u] = d;
	}

	if (!d1[u]) st[u] = true;
	return d1[u];
}

void dfs_u(int u, int fa) {
	for (int i = h[u]; ~i; i = ne[i]) {
		int j = e[i];
		if (j == fa) continue;

		if (p[u] == j) up[j] = max(up[u], d2[u]) + w[i];
		else up[j] = max(up[u], d1[u]) + w[i];

		dfs_u(j, u);
	}
}

int main() {
#ifdef LOCAL
	freopen("data.in", "r", stdin);
#endif
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	memset(h, -1, sizeof h);
	int n;
	cin >> n;

	_for(i, 0, n) {
		int a, b, c;
		cin >> a >> b >> c;
		add(a, b, c);
		add(b, a, c);
	}

	dfs_d(1, -1);
	dfs_u(1, -1);

	int ans = INF;
	_rep(i, 1, n) if (st[i]) ans = min(ans, up[i]);
	else ans = min(ans, max(up[i], d1[i]));

	cout << ans << ENDL;
	return 0;
} 

反思

思考问题要严谨,有一些边界条件一定要考虑到位,一种情况都不能漏。
之前是以为写DP题目不需要预处理,现在发现难一点的DP都需要预处理一下,当自己确定这是一个DP题目,但是有想不出来,有一些东西有矛盾的时候,就可以尝试想想预处理的思考方式!

以上是关于树形DP树的中心的主要内容,如果未能解决你的问题,请参考以下文章

1073. 树的中心(树形dp)

1073. 树的中心(树形dp)

2021-7-21 又回到树形dp的恐怖世界www

2021-7-21 又回到树形dp的恐怖世界www

解题报告树形DP入门

bzoj1907树的路径覆盖 树形dp