树形dp学习笔记

博主分享树形dp入门刷题总结。树形dp是在树上进行动态规划,利用树的父子关系转移。介绍了与《上司的舞会》类似题的状态及转移方程,还提及求树的最小点覆盖的转移方程,以及找树的中心(树的重心)相关问题和性质。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  最近学习树形dp,刷了树形dp入门6题,https://cn.vjudge.net/contest/283650#problem/F,(感谢NEUQ整理)来写一下总结

  树形dp:故名思议,把dp放在 树上进行,利用树的父子关系进行转移

      注意:(1)做题过程中一定要牢记这是一棵树;

         (2)大多数树形dp都是从下往上递归进行(也有从下往上的);

  1.和<上司的舞会>题意类似(一样?)对于每个节点有两种状态,dp[n][0]表示这个点不选的情况下以这个点作为祖先的树的最优情况,dp[n][1]表示选这个点的情况下以这个点作为祖先的树的最优情况.由此,我们可以很快得出转移方程:

  dp[now][1]+=min(dp[to][1],dp[to][0]);

  dp[now][0]+=dp[to][1]//如果不选这个点,那么必选其子节点.

-----------------------------------------------------在去北京的动车上突然很困.....先睡一会,咕咕咕----------------------------------------------------------  

这一咕,就是好久啊....

  2.

  求一棵树的最小点覆盖(要求把一些点染色,使得每个点都邻近一个染色的点或者这个点被染色)

  这个也没什么难度,直接上转移方程

  dp[i][0/1]表示点i没染色/被染色时,以i为根节点的树的被染色点数最小值

  dp[now][0]+=dp[to][1]

  dp[now][1]+=min(dp[to][1],dp[to][0]);

  3.

  给你一棵树,这棵树有n个点,让你找到一些点,把这些点去掉任何一个以后,使得剩下的每一棵树的大小都不超过n/2,找到这个点集.

  分析一下可知:这道题就是要找树的中心.

  复习:

  树的中心有如下性质:  

  1. 树中所有点到某个点的距离和中,到重心的距离和是最小的,如果有两个距离和,他们的距离和一样。
  2. 把两棵树通过一条边相连,新的树的重心在原来两棵树重心的连线上。
  3. 一棵树添加或者删除一个节点,树的重心最多只移动一条边的位置。
  4. 一棵树最多有两个重心,且相邻.
   代码:
  
 1 #include<iostream>
 2 #include<vector>
 3 using namespace std;
 4 const int maxn=1e4+5;
 5 int cnt[maxn];//以自己为祖先的子树的大小
 6 int n;
 7 int dp[maxn];
 8 vector<int>G[maxn];
 9 void dfs(int now,int fa)
10 {
11     cnt[now]=1;
12     for(int i=0;i<G[now].size();++i)
13     {
14         int to=G[now][i];
15         if(to==fa) continue;
16         dfs(to,now);
17         cnt[now]+=cnt[to];
18     }
19     dp[now]=n-cnt[now];//注意此处处理:在树形cnt的时候,记住与一个点相连的不仅是它的儿子,还有它的父亲
20                        //把自己当做祖先,原来的父节点当做一个子节点,求这个子节点(原来的父节点)的数量,用n-cnt[自己]
21     for(int i=0;i<G[now].size();++i)
22     {
23         int to=G[now][i];
24         if(to==fa) continue;
25         dp[now]=max(dp[now],cnt[to]);
26     }
27 }
28 int main()
29 {
30     ios::sync_with_stdio(0);
31     cin>>n;
32     for(int i=2;i<=n;++i)
33     {
34         int a,b;cin>>a>>b;
35         G[a].push_back(b);
36         G[b].push_back(a);
37     }
38     dfs(1,-1);
39     for(int i=1;i<=n;++i)
40     {
41         if(dp[i]<=n/2) cout<<i<<endl;
42     }
43 }

  (ps:一棵树最多两个重心)

 

转载于:https://www.cnblogs.com/codeoosacm/p/10881957.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值