树形DP
作者:互联网
树形 DP,即在树上进行的 DP。
由于树固有的递归性质,树形 DP 一般都是递归进行的。
树的最长路径
题目描述
给定一个含有 n 个节点的 树,以及树中每条边的权值 wedgei。
现需要在树中找出一条路径,使得该路径上所有边的权值之和最大。
思路:
记录以i为根节点的子树中,从子树某个节点到i的最长路和次长路。
那么以经过i的最长边就是最长路+次长路。
而且很明显可以递归求解。
闫氏DP分析法
状态表示—集合\(f_{1,i}\),\(f_{2,i}\): 以节点 i 为根的子树中,从子树某个节点到 i 的最长路为 f1,次长路为 f2
状态表示—属性: 路径长度的最大值 Max
状态计算.......
代码:
#include <bits/stdc++.h>
#define endl '\n'
#define int long long
#define pii pair<int, int>
#define pll pair<int, int>
#define ull unsigned long long
using namespace std;
const int N = 10010, M = N * 2;
int n;
int to[M], w[M], pre[M], h[N], idx;
void add(int a, int b, int c)
{
to[idx] = b, w[idx] = c, pre[idx] = h[a], h[a] = idx++;
}
int ans = 0;
int dfs(int u, int fa)
{
int d1 = 0, d2 = 0;
for (int i = h[u]; i != -1; i = pre[i])
{
int j = to[i];
if (j == fa)
continue;
int d = dfs(j, u)+w[i];
if (d >= d1)
d2 = d1, d1 = d;
else if (d > d2)
d2 = d;
}
ans = max(ans, d1 + d2);
return d1;
}
void solve()
{
memset(h, -1, sizeof h);
cin >> n;
for (int i = 0; i < n-1; i++)
{
int a, b, c;
cin >> a >> b >> c;
add(a, b, c), add(b, a, c);
}
dfs(1, -1);
cout << ans << endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t = 1;
// cin >> t;
while (t--)
solve();
return 0;
}
树的中心 换根DP
题意:
给定一棵树,树中包含 n 个结点(编号1~n)和 n−1 条无向边,每条边都有一个权值。
请你在树中找到一个点,使得该点到树中其他结点的最远距离 最近。
思路:
思考一下:求一个节点的 最远距离 时,会有几种路径呢:
-
从当前节点往下,直到子树中某个节点的最长路径
-
从当前节点往上走到其父节点,再从其父节点出发且不回到该节点的最长路径
引入 换根DP 的思想
换根DP 一般分为三个步骤:
- 指定任意一个根节点
- 一次dfs遍历,统计出当前子树内的节点对当前节点的贡献
- 一次dfs遍历,统计出当前节点的父节点对当前节点的贡献,然后合并统计答案
我们先 dfs 一遍,预处理出当前子树对于根的最大贡献(距离)和 次大贡献(距离)
处理 次大贡献(距离) 的原因是:
如果 当前节点 是其 父节点子树 的 最大路径 上的点。此时 父节点子树 的 最大贡献 不能算作对该节点的贡献,就要使用次大贡献
因为我们的路径是 简单路径(不能 走回头路)
然后我们再 dfs 一遍,求解出每个节点的父节点对他的贡献(即每个节点往上能到的最远路径
两者比较,取一个 max 即可
代码:
主要参考:一只野生彩色铅笔
https://www.acwing.com/user/myspace/index/55909/
标签:int,路径,dfs,树形,节点,DP,d1 来源: https://www.cnblogs.com/kingwz/p/16548828.html