树形dp
Posted showend
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了树形dp相关的知识,希望对你有一定的参考价值。
一般解决方法 根->儿子,再由儿子->根,推出根的值
基本模型:
没有上司的舞会
任意两个一起跳舞的职员层次至少相差2(关系可以看成一棵树,相邻之间不能选择即该节点的父亲和儿子,根节点没有父亲)
一般解法:用f(x)表示x子树进行了决策之后x被选择,能获得的最大权值和,g(x)表示x子树进行了决策之后x没有被选进去,能够得到的最大权值和;
f(x)=(枚举)g(son)+valx,g(x)=(枚举儿子)max{f(son),g(son)}
考虑枚举顺序,先更新儿子。记忆化搜索
#include<iostream> #include<algorithm> using namespace std; #define maxn 6100 struct tree{ int to,nx; }edge[maxn]; int head[maxn],cnt,f[maxn],g[maxn],v[maxn],tag,max1; void add(int u,int v) { edge[++cnt].nx=head[u]; edge[cnt].to=v; head[u]=cnt; } void dfs(int x) { f[x]=v[x]; for(int i=head[x];i;i=edge[i].nx) { if(edge[i].to!=x){ dfs(edge[i].to); f[x]+=g[edge[i].to]; g[x]+=max(g[edge[i].to],f[edge[i].to]); } } } int main() { int n; cin>>n; for(int i=1;i<=n;i++) cin>>v[i]; for(int i=1;i<=n;i++) { int u,v; cin>>v>>u; if(i==n-1) tag=u; add(u,v); } dfs(tag); max1=max(g[tag],f[tag]); cout<<max1; return 0; }
求树的重心(题目是以图的形式给的)
对于一根n个节点的无根树,找到一个点A,使得把树变成以该点为根的有根树时,最大子树的结点树最小。A叫做重心。
求一下每个点子树的点数,再考虑当其作为重心时,最大子树的结点数,和当前重心进行比较;
记忆搜索:
void dfs(int x,int fa)//fa指的是x的父亲 { int t=0;siz[x]=1;//只有一个x节点时,长度为1 for(int i=head[x];i;i=edge[i].nx) { if(edge[i].to!=fa) { dfs(edge[i].to,x);//遍历每个子节点 siz[x]+=siz[edge[i].to]; t=max(t,siz[edge[i].to]);//t记录最大子树的结点数 } t=max(t,n-siz[x]); //比较fa另一侧的子树结点数与当前最大结点数 if(mx>t) { mx=t,poi=x; } } }
树的直径
树的直径是树当中最长的一条链
void dfs(int x,int fa)//fa指的是x的父亲 { for(int i=head[x];i;i=edge[i].nx) { if(edge[i].to!=fa) { int v=edge[i].to; dfs(edge[i].to,x);//遍历每个子节点 if(dp[0][v]+w[v]>dp[0][x])//dp[0][x]代表以0为节点的最长链,1为次长链 { dp[1][x]=dp[0][x];//进行更新,因为一个点只遍历一次,所以最长链上的节点与次长链上的结点不会重合 dp[0][x]=dp[0][v]+w[v]; } else if(dp[0][v]+w[v]>dp[1][x]) dp[1][x]=dp[0][v]+w[v]; } } ans=max(ans,dp[1][x]+dp[0][x]); return; }
以上是关于树形dp的主要内容,如果未能解决你的问题,请参考以下文章
Starship Troopers(HDU 1011 树形DP)