首页 > 其他 > 详细

树形dp总结:从入门到放弃

时间:2019-10-30 15:37:22      阅读:91      评论:0      收藏:0      [点我收藏+]

一、如何拥有一棵树 how to build a tree

存树的方法有很多种,我个人常用的有:

链式前向星存图法

因为树也是一种图,所以说对于存图法是适用的。缺点就是不好利用树的性质。

因为比较普遍,教程也很多,就不再赘述了。

左儿子右兄弟法

 1 struct node //结构体存树
 2 {
 3     int lson,rson;//因为转变为二叉树,所以只有两个儿子
 4 }a[N];//这个好像叫tree更标准
 5 
 6 void findplace(int x,int y)//x是根,y是要插入的儿子
 7 {
 8     int pos=a[x].lson;//这是在x已经有第一个儿子的情况
 9     while(a[pos].rson!=-1)//循环找直到右儿子(兄弟位)空出
10         pos=a[pos].rson;
11     a[pos].rson=y;//成功存入
12 }
13 
14 int main()
15 {
16     for(int i=0;i<n;i++)//初始化
17     {
18         a[i].lson=a[i].rson=-1;
19     }
20     for(int i=1;i<=n;i++)
21     {
22         cin>>x>>y>>w;
23         a[y].val=w;
24         if(a[x].lson==-1)//x还没有儿子就可以充任
25             y=a[x].lson;
26         else
27             findplace(x,y);//否则就适用上面的函数了
28     }
29 }

优点是可以非常方便地遍历整个树,缺点是有点慢。但是在操作系统内部,文件夹之间似乎也是这么存放的呢。

遍历的方法:

void printree(int pos)
{
    cout<<pos<<endl;
    int r=a[pos].rson,l=a[pos].lson;
    while(r!=-1)//先打印兄弟
    {
        printree(r);
        r=a[r].rson;
    }
    if(l!=-1)//再考虑儿子
        printree(l);
}

二、如何状态转移 how to dp

树上的dp一般状态转移都是和子树相关的,而且第一维一般而言都是当前节点的标号。利用记忆化搜索其实能更好利用树,刷表填表我都不太适应……

普通dp比较常用的考虑方法是f[i][0/1]表示点i选/不选的情况下,其子树的最优情况

如果是树形背包,那么f[i][j]表示在以点i为根节点的子树中,选择j个点的最优情况

当然这个要视具体情况分析而定。

一般而言要考虑的问题:叶子节点(也就是边界)、选择和不选择时对应的子节点情况(有什么要求)、根节点怎么处理等。如果没有根节点,可以考虑构建一个虚根;如果题目中是边权,可以考虑下沉至点权来做。注意根节点是不是占用名额!这个是比较重要的。

三、如何水经验 how to get exp

luogu上P201x的题目很多都是树形dp的。个人的成长路线是:

1.没有上司的舞会(经典dp)

2.二叉苹果树(树形背包入门)

3.选课(树上依赖背包入门)

4.战略游戏(普通dp)

5.有线电视网(比较有意思)

 

本蒟蒻比较菜,所以底线就到这里啦。

技术分享图片

树形dp总结:从入门到放弃

原文:https://www.cnblogs.com/YuWenzhou-Sakana/p/11765203.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!