树规理解及题目

树形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代码 
 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 }
View Code
 
  

 题目 HDU - 1520 Anniversary party

 AC代码  用vector数组构建邻接表?

 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 }
View Code

 

 

 

转载于:https://www.cnblogs.com/jjjjjjy/p/11350759.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值