树上dp的入门知识

树的最大独立集

定义:对于一颗n个结点的无根树(没有确定根的树),选出尽量多的结点,使得任意二个结点均不相邻(称为树的最大独立集)。

思路:我们可以任意选一个点作为根结点,然后建树。

   用d(i)表示以i为根结点的子树的最大独立集大小。

对于结点i,只有两种选择,选或者不选,若选,则问题转化为求出i的孙子的d值之,若不选则转化为求i的儿子的d值之和。
状态转移方程:d(i) = max(1 + gs[i], s[i]);
代码:

#include<bits/stdc++.h>
using namespace std;
const int mx=1e5+10;
vector<int> v[maxn];
int d[maxn], s[maxn], gs[maxn],n;
int dfs(int u, int pre)
{
    for(int i = 0; i < v[u].size(); i++)
    {
        int m = v[u][i];
        if(m != pre)
            dfs(m, u);
        s[u] += d[m];
        if(pre != -1)
            gs[pre] += d[m];
    }
    d[u] = max(s[u], gs[u] + 1);
    return d[u];
}
void init()
{
    memset(d, 0, sizeof d);
    memset(s, 0, sizeof s);
    memset(gs, 0, sizeof gs);
    for(int i = 0; i < n; i++)
        v[i].clear();
}
int main()
{
    while(cin >> n)
    {
        init();
        for(int i = 0; i < n-1; i++)
        {
            int a, b;
            cin >> a >> b;;
            v[a].push_back(b);
            v[b].push_back(a);
        }
        cout << dfs(i, -1) << endl;
    }
    return 0;
}

树的重心

定义:树的重心,也称为质心。对于一棵n个结点的无根树,找到一个点(重心),使得把树以该点为根的有根树时,最大子树的结点数最小。换句话说,删除这个点后最大连通块的点(也叫结点)最小。

性质:
1.树中所有点到某个点的距离和中,到重心的距离和是最小的;如果有两个重心,那么他们的距离和一样。
2.把两个树通过一条边相连得到一个新的树,那么新的树的重心在连接原来两个树的重心的路径上。
3.把一个树添加或删除一个叶子,那么它的重心最多只移动一条边的距离。

在这里插入图片描述
如果以1为重心的话,相对于把1删去,分为二堆,{2},{3,4,5,6,7,8,9,10,11},最大子树的结点数为9.
所以我们很容易看出3是重心,{1,2},{4,7,8},{5,9,10},{6,11},最大子树的结点数为3.
还有一个知识点,就是上方子树,顾名思义就是3号结点上方的子树
然后我们怎么求重心呢,其实很简单。找到一个点,其所有的子树中最大的子树节点数最少,跑一遍dfs就好了。
具体看代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cmath>
#include<cstring>
using namespace std;
typedef long long ll;
const int mx=1e5+10;
const double PI=cos(-1.0);
vector<int>G[mx];
int ans,sum,vis[mx],son[mx],n;
void dfs(int u,int pre)
{
    vis[u]=1;
    son[u]=0;
    int maxx=0;
    for(int i=0; i<G[u].size(); i++)
    {
        int v=G[u][i];
        if(v!=pre)
        {
            dfs(v,u);
            son[u]+=son[v]+1;//以u的子树的结点
            maxx=max(maxx,son[v]+1);//儿子,子树的结点
        }
    }
    maxx=max(maxx,n-son[u]-1);//上方子树的结点树,总结点n-(以u的子树的结点+1)
    if(maxx<ans||(ans==maxx&&sum>u))
    {
        ans=maxx;
        sum=u;
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        sum=n;
        ans=n*n;
        for(int i=0; i<=n; i++)
            G[i].clear();
        for(int i=1,x,y; i<n; i++)
        {
            scanf("%d%d",&x,&y);
            G[x].push_back(y);
            G[y].push_back(x);
        }
        dfs(1,0);
        printf("%d %d\n",sum,ans);
    }
    return 0;
}

树的最长路径(最远点对)

定义:对于一课n个结点的无根树,找到一条最长路径。换句话说,要找到二个点,使得他们的距离最远。

其实就是找以i为根结点,找二个最长的子树。相加就好了

#include<bits/stdc++.h>
using namespace std;
const int N = 105;
vector<int>G[N];
int a[N], b[N];//a 存储到达最远结点的长度,b记录次长度
int ans = 0;
void dfs(int u, int pre)
{
    for(int i = 0; i < G[u].size() ;i++)
    {
        if(G[u][i]==pre)continue;
        dfs(G[u][i],u);
        if(a[G[u][i]]+1>=a[u])b[u]=a[u],a[u]=a[G[u][i]]+1;
        else if(a[G[u][i]]+1>b[u])b[u]=a[G[u][i]]+1;
    }

    ans = max(ans, a[u]+b[u]);
    return ;
}
int main()
{
    int n,u,v;
    scanf("%d",&n);
    for(int i=1; i<n; i++)
    {
        scanf("%d %d",&u,&v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    dfs(1,0);
    printf("%d\n",ans);
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
形动态规划(Tree DP)是一种常用的动态规划算法,用于解决结构相关的问题。在Python中,可以使用递归或者迭代的方式实现DPDP的基本思想是,从的叶子节点开始,逐层向上计算每个节点的状态,并利用已经计算过的节点状态来更新当前节点的状态。这样可以通过自底向上的方式,逐步计算出整个的最优解。 下面是一个简单的示例,演示如何使用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、付费专栏及课程。

余额充值