【树形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题目,但是有想不出来,有一些东西有矛盾的时候,就可以尝试想想预处理的思考方式!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值