树形DP
Posted hoyoak
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了树形DP相关的知识,希望对你有一定的参考价值。
考前不写博客就容易颓废QwQ,既然DP比较差就重新总结总结DP,说不定就总结到了...
这一篇总结一下树形DP。
树形DP的转移:儿子到父亲
树具有天然的最有子结构,最有子结构即为儿子。
树形DP的状态:一般设dp[u]表示以u为根的子树的最优子结构。
树形DP可以结合树上的数据结构,同时巧妙运用DFS序、BFS序可以优化解法QwQ。
问题1:树上最大独立集
最大独立集定义:在树上取尽可能多的点,使得所选的点任意两个点都没有边相连。
状态显然有dp[u][0/1],表示以u为根节点的子树选或者没选u的最大独立集,并且可以直接从子树转移来。
dp[u][1]+=dp[e][0];(e是u的儿子)
dp[u][0]+=max(dp[e][0],dp[e][1]);(e是u的儿子)
附一个能过编译随手打的不知道对不对的代码
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> using namespace std; struct node { int ed,nxt; }; node edge[2333<<1]; int n,first[2333],cnt; int dp[2333][2]; inline void add_edge(int s,int e) { ++cnt; edge[cnt].ed=e; edge[cnt].nxt=first[s]; first[s]=cnt; return; } inline void dfs(int now,int fa) { dp[now][1]=1; for(register int i=first[now];i;i=edge[i].nxt) { int e=edge[i].ed; if(e!=fa) { dfs(e,now); dp[now][0]+=max(dp[e][1],dp[e][0]); dp[now][1]+=dp[e][0]; } } return; } int main() { scanf("%d",&n); for(register int i=1;i<=n-1;++i) { int s,e; scanf("%d%d",&s,&e); add_edge(s,e); add_edge(e,s); } dfs(1,0); printf("%d ",max(dp[1][0],dp[1][1])); return 0; }
扩展:最大权独立集,最大独立集可以视为权值为1,最大权独立集改改就好。
问题2:树的直径
树的直径的定义:树上距离最远的两个点的距离。
首先树的直径可以由两遍BFS求出,先从任意一个点出发bfs找到距离这个点最远的那个点,这个点一定是树的直径的
其中一个端点,然后再从那个点出发bfs一遍找到的另一个点就是树的直径的另外一个端点。
考虑DP的做法(显然复杂度更低QwQ)设dp[u]为节点u到所有子树里面的最远距离,然后以经过u的直径就是它的儿子的
dp的最大值+次大值,取max更新答案即可。
inline void dfs(int now,int fa) { for(register int i=first[now];i;i=edge[i].nxt) { int e=edge[i].ed; if(e!=fa) { dfs(e,now); ans=max(ans,dp[e]+dp[u]+edge[i].len); dp[u]=max(dp[u],dp[e]+edge[i].len); } } }
问题3:树的最小点覆盖集
最小点覆盖集:选取最少的点覆盖所有的边
设dp[u][0/1]表示以u为根节点是否选u的子树的最小点覆盖集。
因为要覆盖所有的边所以能轻松得到如果不选这个点的转移dp[u][0]+=dp[e][1];
如果要选择这个点,dp[u][1]+=min(dp[e][0],dp[e][1]);
inline void dfs(int now,int fa) { dp[now][1]=1; for(register int i=first[now];i;i=edge[i].nxt) { int e=edge[i].ed; if(e!=fa) { dfs(e,now); dp[now][0]+=dp[e][1]; dp[now][1]+=min(dp[e][0],dp[e][1]); } } return; }
问题4:树的重心
树的重心的定义:找到一个点,其所有的子树中最大的子树节点数最少,这个点叫做树的重心也叫树的质心。
从定义直接出发即可,设dp[u]表示那个点的最大子树的节点数,取所有节点的min即可。注意必须以u为树根。
inline void dfs(int now,int fa) { siz[now]=1; for(register int i=first[now];i;i=edge[i].nxt) { int e=edge[i].ed; if(e!=fa) { dfs(e,now); siz[now]+=siz[e]; dp[now]=max(dp[now],siz[e]); } } dp[now]=max(dp[now],n-dp[now]); if(dp[now]<dp[ans]) ans=now; return; }
以上是关于树形DP的主要内容,如果未能解决你的问题,请参考以下文章
Starship Troopers(HDU 1011 树形DP)