树形dp
https://blog.csdn.net/qq_39304630/article/details/81836024 //侵删 树形dp整理及入门
https://blog.csdn.net/hjf1201/article/details/78799351 //侵删
https://blog.csdn.net/dcx2001/article/details/78269908 //侵删 树形dp+树形结构总结
def:
树形dp 无环
将所给问题的过程,按时间或空间(树归中是空间,即层数)特征分解成若干相互联系的阶段,以便按次序去求每阶段的解。
步骤: 1.判断是否为 树规 ①判断数据结构是否为 树 ② 是否符合 dp 要求 2.建树 通过数据量和题目要求 选择合适的树的存储方式 邻接矩阵 or 邻接表
①节点数<5000 可使用 邻接矩阵 存储
②节点数>5000 可使用 邻接表 存储 (边要开到2*n 因为是双向)
③二叉树或需要多转二叉 可用两个一维数组 bro[] and child[]存储
3.写出树规方程:通过观察孩子和父亲之间的关系建立方程。我们通常认为,树形DP的写法有两种:
a.根到叶子: 不过这种动态规划在实际的问题中运用的不多。
b.叶子到根: 即根的子节点传递有用的信息给根,完后根得出最优解的过程。这类的习题比较的多。
用途: 1.最大独立子集 最大独立子集的定义是,对于一个树形结构,所有的孩子和他们的父亲存在排斥,也就是如果选取了某个节点,
那么会导致不能选取这个节点的所有孩子节点。 询问是让你给出这颗树的最大独立子集的大小。思路:对于任意一个节点,他都有两种选择: A . 选择:A选择,那么他的孩子必定不能要,此时对于A和A的孩子们来说,能构成的最大独立子集是1+∑A.son.notcome。 B . 不选择:A不选择,那么他的孩子们可来可不来,此时对于A和A的孩子们来说,能构成的最大独立子集是 for i 1->son.size father.notcome+=max(son.notcome,son.come); 当然可以在这基础上加以变化,比如给点设置权值,要你求得这个最大独立子集是满足有最大权值和的。 2.树的重心
题目 HDU-1561 The more, The Better
AC代码
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <iostream> 2 #include <string.h> 3 using namespace std; 4 5 int n,m,a,ans=0,dp[205][205],vis[205],cnt[205],pre[205],f[205][205]; 6 7 //dp[i][j]表示的是是以i为根攻克 第j个城堡 j 必须是 i 的子树 8 // cnt[] 表示 宝物 个数 9 10 struct node{ 11 int v; 12 int next; 13 }c[205]; 14 15 void add(int u,int v){ 16 c[ans].v=v; 17 c[ans].next=pre[u]; 18 pre[u]=ans++; 19 } 20 21 void dfs(int root){ 22 vis[root]=1; 23 for(int i=pre[root];i!=-1;i=c[i].next){ 24 int u=c[i].v; 25 if(!vis[u]){ 26 dfs(u); 27 for(int k=m;k>=0;k--) 28 for(int j=0;j<=k;j++) 29 f[root][k]=max(f[root][k],f[root][k-j]+dp[u][j]); 30 } 31 } 32 for(int i=1;i<=m+1;i++) 33 dp[root][i]=f[root][i-1]+cnt[root]; 34 } 35 36 int main(){ 37 while(cin>>n>>m&&n&&m){ 38 ans=cnt[0]=0; 39 memset(f,0,sizeof(f)); 40 memset(dp,0,sizeof(dp)); 41 memset(pre,-1,sizeof(pre)); // 初始化 为 -1 的原因 42 memset(vis,0,sizeof(vis)); 43 for(int i=1;i<=n;i++){ 44 cin>>a>>cnt[i]; 45 add(a,i); 46 } 47 dfs(0); 48 cout<<dp[0][m+1]<<endl; 49 } 50 return 0; 51 }
题目 HDU - 1520 Anniversary party
AC代码 用vector数组构建邻接表?
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include<iostream> 2 #include<vector> 3 4 using namespace std; 5 6 int n,l,k,r[6010],dp[6010][2],f[6010]; 7 vector <int> v[6010]; 8 9 //dp[i][0] 表示不选 i //dp[i][1] 表示OPT(i) 10 11 void dfs(int cur){ 12 dp[cur][1]=r[cur]; 13 for(int i=0;i<v[cur].size();i++){ //只需一维 14 int son=v[cur][i]; 15 dfs(son); 16 dp[cur][0]+=max(dp[son][0],dp[son][1]); 17 dp[cur][1]+=dp[son][0]; 18 } 19 } 20 21 int main(){ 22 while(cin>>n){ 23 for(int i=1;i<=n;i++){ 24 cin>>r[i]; 25 v[i].clear(); 26 dp[i][0]=dp[i][1]=0; 27 f[i]=-1; 28 } 29 while(cin>>l>>k&&l!=0&&k!=0){ 30 v[k].push_back(l); 31 f[l]=k; //f[]=-1时 代表 root 32 } 33 int t=1; 34 while(f[t]!=-1){ 35 t=f[t]; 36 } //相当于一个相互映射 从 child 找到 father 最后找到 root 37 dfs(t); 38 cout<<max(dp[t][0],dp[t][1])<<endl; 39 } 40 return 0; 41 }