树形Dp

说一下树形DP

一般来说树形DP会与背包问题结合,尤其是分组背包 why?因为每一个子树可以看成一个组。

树形+背包

树形+分组背包:
选课
核心代码:

void dfs(int u){
    f[u][0]=0;
    for(int i=0;i<a[u].size();i++){
        int v=a[u][i];
        dfs(v); 
        for(int j=m;j>=0;j--)
            for(int k=1;k<=j;++k)
            f[u][j]=max(f[u][j],f[u][j-k]+f[v][k]);
    }
    if(u)
        for(int i=m;i>0;--i)
            f[u][i]=f[u][i-1]+b[u];
}			

有线电视网
核心代码:

int dfs(int u){
    if(u>n-m){
        f[u][1]=b[u];
        return 1;
    }
    int sum=0,t;
    for(int i=h[u];i;i=a[i].ne){
        int v=a[i].to;
        t=dfs(v); sum+=t;
        for(int j=sum;j>0;j--)//倒序可以理解成滚动数组
            for(int k=1;k<=t;++k)
                if(k<=j)
                f[u][j]=max(f[u][j],f[u][j-k]+f[v][k]-a[i].v);
    }
    return sum;
}
树的最大独立集

战略游戏

void dfs(int u,int fa){
    e[u]=1;
    f[u][1]=1; f[u][0]=0;
    for(int i=h[u];i;i=a[i].ne){
        int v=a[i].to;
        if(v==fa) continue;
        if(e[v]) continue;
            dfs(v,u);
            f[u][0]+=f[v][1];
            f[u][1]+=min(f[v][0],f[v][1]);
    }
}

没有上司的舞会
这题我个人理解也是求树的最大独立集

void dfs(int u,int fa){
    e[u]=1;
    for(int i=h[u];i;i=a[i].ne){
        int v=a[i].to;
        if(v==fa) continue;
        if(!e[v])
            dfs(v,u); 
        f[u][1]=max(f[u][1],f[u][1]+f[v][0]);
        f[u][0]=max(f[u][0],f[u][0]+f[v][1]);
        f[u][1]=max(f[u][1],f[u][0]);
    }
}
最小支配集

洛谷p2899
和最大独立集有点像
核心代码:

void dfs(int u,int fa){
    f[u][0]=1;  f[u][2]=0; f[u][2]=0x7fffffff;
    for(int i=h[u];i;i=a[i].ne){
        int v=a[i].to;
        if(v==fa) continue;
        dfs(v,u);
        f[u][0]+=min(min(f[v][1],f[v][0]),f[v][2]);
        f[u][1]+=min(f[v][2],f[v][0]);
    }
    for(int i=h[u];i;i=a[i].ne){
        int v=a[i].to;
        if(v==fa) continue;
        f[u][2]=min(f[u][2],f[u][1]-min(f[v][0],f[v][1])+f[v][0]);
    }
}
树的重心
定义

树的重心:也叫树的质心。找到一个点,其所有的子树中最大的子树节点数最少,那么这个点就是这棵树的重心,删去重心后,生成的多棵树尽可能平衡。

性质

性质 1 :树中所有点到重心的距离和是最小的,一棵树最多有两个重心(比如就两个点连个边)。

性质 2 :把两棵树通过加一条边得到一颗新的树,新的树的重心必然在连接原来两棵树重心的路径上。

性质 3 :一棵树添加或者删除一个节点,树的重心最多只移动一条边的位置。

若有一点,其所有子树中最大子树的节点数最少,则该点就是这棵树的重心。
一般的树只有一个重心,有些有偶数个节点的树,有两个节点。
求树的重心方法就是随意确定一个根节点,先把无根树转化为有根树,dfs求出所有点的子树的节点个数。如果有一点满足该点的子树的节点数的二倍大于等于总结点数(size[u]*2>=n),并且该点的儿子都满足子树的节点数的二倍小于等于总结点数(size[son_u]*2<=n),这个点就是树的重心。

#include<cstdio>
#define N 42000
int n,a,b,next[N],to[N],head[N],num,size[N],father[N],ans;
void add(int false_from,int false_to){
    next[++num]=head[false_from];
    to[num]=false_to;
    head[false_from]=num;
}
void dfs(int x){
    size[x]=1;
    for(int i=head[x];i;i=next[i])
        if(father[x]!=to[i]){
            father[to[i]]=x;
            dfs(to[i]);
            size[x]+=size[to[i]];
        }
    if(size[x]*2>=n&&!ans)
        ans=x;
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<n;++i){
        scanf("%d%d",&a,&b);
        add(a,b);
        add(b,a);
    }
    dfs(1);
    printf("%d",ans);
    return 0;
}
树的直径

推荐阅读这

列出一些经典问题吧:
:     
1.给出一棵树 每个节点有权值  要求父节点和子节点不能同时取 求能够取得的最大值  (hdu1520)  
2.给出一棵树,求离每个节点最远的点的距离 (hdu2196)   
3
 3.1在一个地图上,有N座城堡,每座城堡都有一定的宝物,在每次游戏中允许攻克M个城堡并获得里面的宝物。但由于 地理位置原因,有些城堡不能直接攻克,要攻克这些城堡必须先攻克其他某一个特定的城堡。求获得尽量多的宝物应  该攻克哪M个城堡。 (hdu1561)
题解:树形dp+背包
 3.2每个节点有两个值bug和brain,当清扫该节点的所有bug时就得到brain值,只有当父节点被清空时,才可以清扫它的子节点,而清扫需要一定的人员。给定M个人员,N个结点的树,求最大brain和 (hdu1011)
 3.3现在有n个村子,你想要用收买m个村子为你投票,其中收买第i个村子的代价是val[i]。但是有些村子存在从属关系,如果B从属于A国,则收买了A也意味着买通了B,而且这些关系是传递的。问你最小要付出的代价是多少? (poj3345)  
4:
 4.1 一棵树,定义每个节点的balance值:去掉这点节点后的森林里所有树的最大节点数。求出最小的balance值和其所对应的节点编号(poj1655)
 4.2 给你一棵无向树 T,要求依次去除树中的某个结点,求去掉该结点后变成的森林 T' 中的最大分支。并要求该分支为 去除的结点尽可能少。答案可能有多个,需要按照节点编号从小到大输出 (poj3107)

5.给一棵树, n结点<=1000, 和K< =200,  在这棵树上找大小为k的子树, 使其点权和值最大 (zoj3201)  
6.给一个树状图,有n个点。求出,去掉哪个点,使得剩下的每个连通子图中点的数量不超过n/2。如果有很多这样的 点,就按升序输出。n<=10000 (poj2378)  
7.一棵n个结点的带权无根树,从中删去一条边,使得剩下来的两棵子树的节点权值之和的绝对值最小,并求出得到的最小绝对值 (poj3140)  
8.给出一些点,有值,给出一些边,然后求去掉一条边后将分成连通的两部分,且两部分的差值最小 (hdu2242)  
9.有n个点组成一个树,问至少要删除多少条边才能获得一棵有p个结点的子树 (poj1947)  
10.一棵树n<=1000(节点的分支<=8),Snail在根处,它要找到在某个叶子处的house而其中一些节点上有worm,worm会告诉它的house是否在这个子树上求Snail最快寻找到house走过路径的期望值 (poj2057)  
11.给你一颗苹果树,有N个节点每个节点上都有一个苹果也就是有一个权值,当你经过这个节点是你将得到这个权值,重复走节点是只能算一次,给你N-1条边。问你只能走K步能得到的最大权值和 (poj2486)  
12.一颗二叉苹果树树上结苹果,要求剪掉几棵枝,然后求保留Q根树枝能保留的最多到苹果数 (ural1018)  
13.给定一棵树,求最少连多少条边,使得每个点在且仅在某一个环内。 (poj1848)  
14.在一棵树形的城市中建立一些消防站,但每个城市有一个最大距离限制,求需要的最小花费 (poj2152)
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
树形动态规划(Tree DP)是一种常用的动态规划算法,用于解决树结构相关的问题。在Python中,可以使用递归或者迭代的方式实现树形DP树形DP的基本思想是,从树的叶子节点开始,逐层向上计算每个节点的状态,并利用已经计算过的节点状态来更新当前节点的状态。这样可以通过自底向上的方式,逐步计算出整个树的最优解。 下面是一个简单的示例,演示如何使用树形DP解决一个二叉树中节点权值之和的最大值问题: ```python class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right def max_sum(root): if root is None: return 0 # 递归计算左右子树的最大权值和 left_sum = max_sum(root.left) right_sum = max_sum(root.right) # 当前节点的最大权值和为当前节点值加上左右子树中较大的权值和 return root.val + max(left_sum, right_sum) # 构建一个二叉树 root = TreeNode(1) root.left = TreeNode(2) root.right = TreeNode(3) root.left.left = TreeNode(4) root.left.right = TreeNode(5) # 计算二叉树中节点权值之和的最大值 result = max_sum(root) print(result) ``` 这段代码中,我们定义了一个`TreeNode`类来表示二叉树的节点,其中`val`表示节点的权值,`left`和`right`分别表示左子节点和右子节点。`max_sum`函数使用递归的方式计算二叉树中节点权值之和的最大值,通过比较左右子树的最大权值和来确定当前节点的最大权值和。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值