树形dp学习笔记
最近学习树形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 #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:一棵树最多两个重心)
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步