树形dp的几种问题

树的最大独立集

题目描述

对于一棵\(n\)个结点的无根树,选出尽可能多的结点,使得任意两个结点均不相邻(称为最大独立集),然后输入\(n-1\)条无向边,输出一个最大独立集。

解决方法

  • 对于无根树,只要任选一个根,无根树就变成了有根树

  • 结点\(i\)有两种状态:选和不选。如果不选\(i\),问题就转化为求出\(i\)的所有儿子的\(dp\)值再相加;如果选\(i\),问题就转化成\(i\)的所有孙子的\(dp\)值之和

  • 状态转移方程为:

\[dp(i) = max \left\{ \begin{aligned} 1 + \sum_{j \in gs(i)} dp(j),\sum_{j \in s(i)} dp(j) \end{aligned} \right\} \]

代码编写

void dfs(int u)
{
    vis[u] = 1 ;
    for(auto v:son[u])
    {
        if(vis[v] == 1) continue ;
        dfs(v) ;
        dp[u][1] += dp[v][0] ;
        dp[u][0] += max(dp[v][0], dp[v][1]) ;
    }
    return ;
}

树的重心

题目描述

对于一棵n个结点的无根树,找到一个点,使得把树变成以该点为根的有根树时,最大子树的结点数最小。

解决思路

  • 先以任意结点为根,将无根树转为有根树。

  • \(dp_i\)表示以i为根的子树的结点个数。所以

\[dp[i]=\sum_{j \in s(i)} dp[j] + 1 \]

  • 删除结点\(i\)后,最大连通块有\(dp[j]_{max}\)个结点

实现

void dfs(int u)
{
  vis[u] = 1 ;
  for(auto v:son[u])
  {
    if(vis[v] == 1) continue ;
    dfs(v) ;
    dp[u] = dp[v] + 1 ;
  }
}

树的最远点对

题目描述

对于一棵\(n\)个结点的无根树,找到一条最长路径。(找到两个点,使得它们距离最远)

解决思路

  • 先把无根树转为有根树。

  • 对于任意结点\(i\),经过\(i\)的最长路就是连接\(i\)的两棵不同子树\(u\)\(v\)的最深叶子的路径

实现

posted @ 2023-04-29 02:56  Lwen1243  阅读(21)  评论(0)    收藏  举报