树形DP习题

P1352 没有上司的舞会(树形DP入门题)

在这里插入图片描述

题意:给定一棵树,每个节点都有自己的权值,父子节点之间只能选择一个,问怎样可以使得整棵树选到的权值最大?
思路:设 d p [ u ] [ 0 / 1 ] dp[u][0/1] dp[u][0/1], 0 表示不选择 u 节点,1 表示选择 u 节点。可以这样转移:

  • d p [ u ] [ 0 ] = ∑ m a x ( d p [ v ] [ 0 ] , d p [ v ] [ 1 ] ) dp[u][0]=\sum max(dp[v][0],dp[v][1]) dp[u][0]=max(dp[v][0],dp[v][1])
  • d p [ u ] [ 1 ] = w [ u ] + ∑ d p [ v ] [ 0 ] dp[u][1]=w[u]+ \sum dp[v][0] dp[u][1]=w[u]+dp[v][0]
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=6000+10,maxm=5e5+10;

int n;
int a[maxn];
vector<int> e[maxn];
int in[maxn];
int dp[maxn][2];

void dfs(int u,int fa=-1)
{
    for(auto v: e[u])
    {
        if(v==fa) continue;
        dfs(v,u);
        dp[u][0]+=max(dp[v][0],dp[v][1]);
        dp[u][1]+=dp[v][0];
    }
    dp[u][1]+=a[u];
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;++i) scanf("%d",&a[i]);
    for(int i=1;i<=n-1;++i)
    {
        int u,v;
        scanf("%d%d",&v,&u);
        e[u].push_back(v);
        in[v]++;
    }
    int u=0;
    for(int i=1;i<=n;++i)
        if(in[i]==0) u=i;
    dfs(u);
    ll ans=max(dp[u][0],dp[u][1]);
    printf("%lld\n",ans);
    return 0;
}

Strategic game POJ - 1463 (树形DP入门题)

链接:http://poj.org/problem?id=1463

题意:给定一颗有向树,问至少需要选择多少个点,才能使得所有的边都和选择的点相连
思路:对点只有选和不选两种情况,设 d p [ u ] [ 0 / 1 ] dp[u][0/1] dp[u][0/1] ,0表示不选,1表示选择

  • d p [ u ] [ 0 ] = ∑ d p [ v ] [ 1 ] dp[u][0]=\sum dp[v][1] dp[u][0]=dp[v][1]
  • d p [ u ] [ 1 ] = 1 + ∑ m i n ( d p [ v ] [ 0 ] , d p [ v ] [ 1 ] ) dp[u][1]=1+\sum min(dp[v][0],dp[v][1]) dp[u][1]=1+min(dp[v][0],dp[v][1])
#include <cstdio>
#include <vector>
#include <cstring>
#define ll long long
using namespace std;
const int maxn=3000+10,maxm=5e5+10;

int n;
vector<int> e[maxn];
int dp[maxn][2],in[maxn];

void dfs(int u,int fa=-1)
{
    for(int i=0;i<e[u].size();++i)
    {
        int v=e[u][i];
        if(v==fa) continue;
        dfs(v,u);
        dp[u][0]+=dp[v][1];
        dp[u][1]+=min(dp[v][0],dp[v][1]);
    }
    dp[u][1]++;
}

int main()
{
    while(~scanf("%d",&n))
    {
        memset(dp,0,sizeof(dp));
        memset(in,0,sizeof(in));
        for(int i=1;i<=n;++i) e[i].clear();
        for(int i=1;i<=n;++i)
        {
            int u,v,x;
            scanf("%d:(%d)",&u,&x);
            ++u;
            for(int j=1;j<=x;++j)
            {
                scanf("%d",&v);
                ++v;
                e[u].push_back(v),in[v]++;
            }
        }
        int rt=-1;
        for(int i=1;i<=n;++i)
            if(in[i]==0) rt=i;
        dfs(rt);
        printf("%d\n",min(dp[rt][0],dp[rt][1]));
    }
    return 0;
}
/*
8
0:(2) 1 2
1:(2) 3 4
2:(3) 5 6 7
3:(0)
4:(0)
5:(0)
6:(0)
7:(0)
*/

Computer HDU - 2196 (树形DP经典题)

链接:http://acm.hdu.edu.cn/showproblem.php?pid=2196

题意:给定一个无向联通图,求离每个点最远的点的距离

思路:设 d p [ u ] [ 0 ] dp[u][0] dp[u][0] 表示在以 u u u 为根的子树中距离 u u u 最远的距离, d p [ u ] [ 1 ] dp[u][1] dp[u][1] 表示在以 u 为根的子树中距离 u 第二远的距离。 d p [ u ] [ 2 ] dp[u][2] dp[u][2] 表示通过 u 的父节点,得到距离 u 的最远距离

  • d p [ u ] [ 0 / 1 ] dp[u][0/1] dp[u][0/1] 中 u 通过 v 来更新,先往下走,在更新。
  • d p [ u ] [ 2 ] dp[u][2] dp[u][2] 中 v 通过 u 来更新,先更新,再往下走。 d p [ v ] [ 2 ] dp[v][2] dp[v][2] 有两种获得途径,一是父节点往上走,二是父节点不通过 v 往下走 。(这也是为什么要求两个最远和次远距离的原因)
    d p [ v ] [ 2 ] = m a x ( d p [ u ] [ 0 ] + w , d p [ u ] [ 2 ] + w ) dp[v][2]=max(dp[u][0]+w,dp[u][2]+w) dp[v][2]=max(dp[u][0]+w,dp[u][2]+w) (最远距离不通过 v )
    d p [ v ] [ 2 ] = m a x ( d p [ u ] [ 1 ] + w , d p [ u ] [ 2 ] + w ) dp[v][2]=max(dp[u][1]+w,dp[u][2]+w) dp[v][2]=max(dp[u][1]+w,dp[u][2]+w) (最远距离通过v,用次远距离来更新)

实现:最远和次远距离如何更新?设 d=dis[v][0]+w,,先用当前距离 d 更新最远距离d0,不能对最远距离做更新时,用 d 来更新 d1

PS:好像可以树分治,下次在学,咕咕咕。(update:2020年10月24日,点分治不行,点分治只能计算所有路径不能更新回去)

#include <bits/stdc++.h>
#define fi first
#define se second
#define ll long long
using namespace std;
const int maxn=1e4+5;

int n;
vector<pair<int,int> > e[maxn];
ll dis1[maxn],dis2[maxn],dis3[maxn];
int son[maxn];

void dfs1(int u,int fa)
{
    ll d=0,d1=0,d2=0;
    for(auto x: e[u])
    {
        int v=x.fi,w=x.se;
        if(v==fa) continue;
        dfs1(v,u);
        d=dis1[v]+w;
        if(d1<=d)
        {
            d2=d1;
            d1=d;
            son[u]=v;
        }
        else if(d2<d) d2=d;
    }
    dis1[u]=d1,dis2[u]=d2;
}
void dfs2(int u,int fa)
{
    for(auto x: e[u])
    {
        int v=x.fi,w=x.se;
        if(v==fa) continue;
        if(son[u]==v) dis3[v]=max(dis2[u]+w,dis3[u]+w);
        else dis3[v]=max(dis1[u]+w,dis3[u]+w);
        dfs2(v,u);
    }
}
int main()
{
    while(~scanf("%d",&n))
    {
        for(int i=1; i<=n; ++i) e[i].clear();
        memset(dis1,0,sizeof(dis1));
        memset(dis2,0,sizeof(dis2));
        memset(dis3,0,sizeof(dis3));
        for(int v=2; v<=n; ++v)
        {
            int u,w;
            scanf("%d%d",&u,&w);
            e[u].push_back({v,w});
            e[v].push_back({u,w});
        }
        dfs1(1,0);
        dfs2(1,0);
        for(int i=1; i<=n; ++i)
            printf("%lld\n",max(dis1[i],dis3[i]));
    }
    return 0;
}

Accumulation Degree POJ - 3585 (树形DP经典入门题)

链接:http://poj.org/problem?id=3585

题意:给定一颗无根树,每条边都有边权,让你求所有叶节点到每个点的最大流量之和

思路:每个点的流量总共两种来源,一是来自父节点,二是来自所有子节点。设 d p [ u ] [ 0 / 1 ] dp[u][0/1] dp[u][0/1] d p [ u ] [ 0 ] dp[u][0] dp[u][0] 代表所有来自子节点的流量, d p [ u ] [ 1 ] dp[u][1] dp[u][1] 表示来自父节点的流量

  • d p [ u ] [ 0 ] = ∑ m i n ( d p [ v ] [ 0 ] , w ) dp[u][0]= \sum min(dp[v][0],w) dp[u][0]=min(dp[v][0],w)
  • 当 u 为根,且入度为 1 时,它的流量是无穷的, d p [ v ] [ 1 ] = w dp[v][1]=w dp[v][1]=w,(这里我设 1 为根)
  • 当 u 为根,且入度大于 1 时,它的流量受另一条边控制。这种情况和 u 不为 根时相同, d p [ v ] [ 1 ] = m i n ( d p [ u ] [ 0 ] + d p [ u ] [ 1 ] − m i n ( d p [ v ] [ 0 ] , w ) , w ) dp[v][1] = min(dp[u][0]+dp[u][1]- min(dp[v][0],w),w) dp[v][1]=min(dp[u][0]+dp[u][1]min(dp[v][0],w),w)
#include <cstdio>
#include <vector>
#include <cstring>
#define fi first
#define se second
#define ll long long
using namespace std;
const int maxn=2e5+10,maxm=5e5+10,inf=1e9;

int t,n;
int head[maxn],cnt;
struct Edge
{
    int nxt,to,w;
} edges[maxn<<1];
void add(int u,int v,int w)
{
    edges[++cnt].to=v;
    edges[cnt].w=w;
    edges[cnt].nxt=head[u];
    head[u]=cnt;
}
ll dp1[maxn],dp2[maxn];
int in[maxn];

void dfs1(int u,int fa)
{
    for(int i=head[u]; i!=-1; i=edges[i].nxt)
    {
        int v=edges[i].to,w=edges[i].w;
        if(v==fa) continue;
        dfs1(v,u);
        if(in[v]==1) dp1[u]+=w;
        else dp1[u]+=min(dp1[v],1ll*w);
    }
}
void dfs2(int u,int fa=-1)
{
    for(int i=head[u]; i!=-1; i=edges[i].nxt)
    {
        int v=edges[i].to,w=edges[i].w;
        if(v==fa) continue;
        ll flow=min(1ll*w,dp1[v]);
        if(in[1]==1&&u==1) dp2[v]=w;
        else dp2[v]+=min(dp1[u]+dp2[u]-flow,1ll*w);
        dfs2(v,u);
    }
}
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        cnt=0;
        memset(head,-1,sizeof(head));
        memset(dp1,0,sizeof(dp1));
        memset(dp2,0,sizeof(dp2));
        memset(in,0,sizeof(in));
        for(int i=1; i<=n-1; ++i)
        {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            add(u,v,w);
            add(v,u,w);
            in[u]++,in[v]++;
        }
        dfs1(1,0);
        dfs2(1,0);
        ll ans=0;
        for(int i=1; i<=n; ++i) ans=max(ans,dp1[i]+dp2[i]);
        printf("%lld\n",ans);
    }
    return 0;
}

D. Distance in Tree CF161D

链接:https://codeforces.com/problemset/problem/161/D

题意:给定一颗无根树,求两个点之间距离为 k 的对数。
思路:设 d p [ u ] [ i ] [ 0 / 1 ] dp[u][i][0/1] dp[u][i][0/1] 分别表示以 u 为根的子树中距离 u 为 i 的节点数,和 u 的祖先中距离 u 为 i 个节点数,最终答案就是 ∑ d p [ u ] [ k ] [ 0 ] + d p [ u ] [ k ] [ 1 ] \sum dp[u][k][0] +dp[u][k][1] dp[u][k][0]+dp[u][k][1]

  • d p [ u ] [ i ] [ 0 ] = d p [ v ] [ i − 1 ] [ 0 ] dp[u][i][0] =dp[v][i-1][0] dp[u][i][0]=dp[v][i1][0],注意初始值 dp[u][0][0]=1,这里不赋值对往上推有影响。(这里的细节卡了挺久的)
  • d p [ v ] [ i ] [ 1 ] = d p [ u ] [ i − 1 ] [ 1 ] + d p [ u ] [ i − 1 ] [ 0 ] − d p [ v ] [ i − 2 ] [ 0 ] dp[v][i][1]=dp[u][i-1][1]+dp[u][i-1][0]-dp[v][i-2][0] dp[v][i][1]=dp[u][i1][1]+dp[u][i1][0]dp[v][i2][0], 与 v v v 距离为 k k k 的点为,它的父节点距离为 k − 1 k-1 k1 的点,同时要删去经过 v 重复计数的点。
#include <cstdio>
#include <vector>
#include <cstring>
#define ll long long
using namespace std;
const int maxn=5e4+10,maxm=500+10,inf=1e9;

int n,k;
vector<int> e[maxn];
int dp[maxn][maxm][2];

void dfs1(int u,int fa=-1)
{
    dp[u][0][0]=1;
    for(auto v: e[u])
    {
        if(v==fa) continue;
        dfs1(v,u);
        for(int i=1;i<=k;++i)
            dp[u][i][0]+=dp[v][i-1][0];
    }
}
void dfs2(int u,int fa=-1)
{
    for(auto v: e[u])
    {
        if(v==fa) continue;
        dp[v][1][1]=1;
        for(int i=2;i<=k;++i)
            dp[v][i][1]=dp[u][i-1][1]+dp[u][i-1][0]-dp[v][i-2][0];
        dfs2(v,u);
    }
}
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n-1;++i)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        e[u].push_back(v);
        e[v].push_back(u);
    }
    dfs1(1);
    dfs2(1);
    ll ans=0;
    for(int i=1;i<=n;++i)
        ans+=dp[i][k][0]+dp[i][k][1];
    printf("%lld\n",ans/2);
    return 0;
}

P2986 [USACO10MAR]Great Cow Gathering G

链接:https://www.luogu.com.cn/problem/P2986

题意:给定一颗无根树,每个点有点权,每条边有边权。一个点移动到另一个点的代价为 点权 乘上 距离。让你选择一个点作为中心,让其他点到这个点的代价和最小。求出这个代价和
思路:我们可以设 1 为根,然后计算出其他点到 1 的代价和,然后从根倒推回去。假设从 u 推到 v ,可以先让原本 v 到 u 的点退回来,然后让其他的点达到 v 。

  • d i s 1 [ u ] dis1[u] dis1[u] 表示以 u 为根的子树中,所有点到 u 的代价。 d i s 1 [ u ] = ∑ d i s 1 [ v ] + w × n u m [ v ] dis1[u]=\sum dis1[v]+w \times num[v] dis1[u]=dis1[v]+w×num[v],同时更新数量 n u m [ u ] = ∑ n u m [ v ] num[u]=\sum num[v] num[u]=num[v]
  • d i s 2 [ u ] dis2[u] dis2[u] 表示到达 1 后倒推回来,那么 d i s 2 [ v ] = d i s [ u ] − n u m [ v ] × w + ( s u m − n u m [ v ] ) × w dis2[v]=dis[u] - num[v]\times w + (sum-num[v])\times w dis2[v]=dis[u]num[v]×w+(sumnum[v])×w
#include <cstdio>
#include <vector>
#include <cstring>
#define ll long long
using namespace std;
const int maxn=1e5+10,maxm=500+10;

int head[maxn],cnt;
struct Edge
{
    ll nxt,to,w;
}edges[maxn<<1];
void add(int u,int v,int w)
{
    edges[++cnt].to=v;
    edges[cnt].w=w;
    edges[cnt].nxt=head[u];
    head[u]=cnt;
}
ll n,num[maxn];
ll dis[maxn][2];
void dfs1(int u,int fa=-1)
{
    for(int i=head[u];i!=-1;i=edges[i].nxt)
    {
        int v=edges[i].to,w=edges[i].w;
        if(v==fa) continue;
        dfs1(v,u);
        dis[u][0]+=dis[v][0]+w*num[v];
        num[u]+=num[v];
    }
}
void dfs2(int u,int fa=-1)
{
    for(int i=head[u];i!=-1;i=edges[i].nxt)
    {
        ll v=edges[i].to,w=edges[i].w;
        if(v==fa) continue;
        dis[v][1]=dis[u][1]-num[v]*w+(num[1]-num[v])*w;
        dfs2(v,u);
    }
}
int main()
{
    scanf("%lld",&n);
    for(int i=1;i<=n;++i) scanf("%lld",&num[i]);
    cnt=0;
    memset(head,-1,sizeof(head));
    for(int i=1;i<=n-1;++i)
    {
        ll u,v,w;
        scanf("%lld%lld%lld",&u,&v,&w);
        add(u,v,w);
        add(v,u,w);
    }
    dfs1(1);
    dfs2(1);
    ll ans=9e18;
    for(int i=1;i<=n;++i)
        ans=min(ans,dis[1][0]+dis[i][1]);
    printf("%lld\n",ans);
    return 0;
}

D. Fish eating fruit 2019沈阳网络预选赛(树形DP || 点分治)

链接:https://www.jisuanke.com/contest/3007/challenges

题意:给定一棵树求所有点对,模3意义下的总权值之和。
思路

  • d p [ u ] [ k ] dp[u][k] dp[u][k] 表示以 u u u 为根的子树中,所有子节点与 u u u 的距离模 3 为 k k k 的总和
  • n u m [ u ] [ k ] num[u][k] num[u][k] 表示以 u u u 为根的子树中,所有子节点与 u u u 的距离模 3 为 k k k 的子节点数
  • 以子节点来更新父节点。每次计算以 u 为根的子树中,一个节点为 v,另个一节点在与 v 不同的子树中的贡献。这样就需要先对答案做更新,然后再把当前遍历到的 v 节点,转移到 u 上。
  • 计算答案:相当于计算所有路径路径类似 x - u - v - y 形式的所有方案,把它分成了3份,x - u 这一段的总和为 d p [ u ] [ j ] dp[u][j] dp[u][j],连接的数量为 n u m [ v ] [ k ] num[v][k] num[v][k],v - y 这段的总和为 d p [ v ] [ j ] dp[v][j] dp[v][j] ,另一边连接的节点数为 n u m [ u ] [ k ] num[u][k] num[u][k]
    u - v 这段为: n u m [ u ] [ j ] × n u m [ v ] [ k ] × w num[u][j]\times num[v][k] \times w num[u][j]×num[v][k]×w
    在更新前计算答案: d p [ u ] [ j ] × n u m [ v ] [ k ] + d p [ v ] [ k ] + n u m [ u ] [ j ] dp[u][j]\times num[v][k] + dp[v][k]+num[u][j] dp[u][j]×num[v][k]+dp[v][k]+num[u][j] d p [ u ] [ j ] dp[u][j] dp[u][j]此时并不包含子树 v 的信息。
  • 转移方程: d p [ u ] [ ( i + w ) % 3 ] = ∑ d p [ v ] [ i ] + w × n u m [ v ] [ i ] dp[u][ (i+w)\%3]=\sum dp[v][i] + w \times num[v][i] dp[u][(i+w)%3]=dp[v][i]+w×num[v][i]
  • n u m [ u ] [ ( i + w ) % 3 ] = ∑ n u m [ v ] [ j ] num[u][(i+w)\%3]=\sum num[v][j] num[u][(i+w)%3]=num[v][j]

参考链接: 倒推法

#include <cstdio>
#include <vector>
#include <cstring>
#define ll long long
using namespace std;
const int maxn=1e4+10,maxm=500+10,mod=1e9+7;

int head[maxn],cnt;
struct Edge
{
    int nxt,to,w;
}edges[maxn<<1];
void add(int u,int v,int w)
{
    edges[++cnt].to=v;
    edges[cnt].w=w;
    edges[cnt].nxt=head[u];
    head[u]=cnt;
}
ll ans[3],dp[maxn][3],num[maxn][3];
void dfs(int u,int fa=-1)
{
    for(int i=head[u];i!=-1;i=edges[i].nxt)
    {
        int v=edges[i].to,w=edges[i].w;
        if(v==fa) continue;
        dfs(v,u);
        for(int j=0;j<=2;++j)
        {
            for(int k=0;k<=2;++k)
            {
                int x=(j+k+w)%3;
                ans[x]=(ans[x]+num[u][j]*num[v][k]%mod*w%mod)%mod;
                ans[x]=(ans[x]+num[u][j]*dp[v][k]%mod+num[v][k]*dp[u][j]%mod)%mod;
            }
        }
        for(int j=0;j<=2;++j)
        {
            int x=(j+w)%3;
            dp[u][x]=(dp[u][x]+dp[v][j]+num[v][j]*w%mod)%mod;
            num[u][x]+=num[v][j];
        }
    }
}
int n;
int main()
{
    while(~scanf("%d",&n))
    {
        memset(ans,0,sizeof(ans));
        memset(dp,0,sizeof(dp));
        memset(num,0,sizeof(num));
        cnt=0;
        for(int i=1;i<=n;++i) head[i]=-1;
        for(int i=1;i<=n-1;++i)
        {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            u++,v++;
            add(u,v,w);
            add(v,u,w);
        }
        for(int i=1;i<=n;++i) num[i][0]=1;
        dfs(1);
        for(int i=0;i<=2;++i)
            printf("%lld%c",ans[i]*2%mod,i==2?'\n':' ');
    }
    return 0;
}

如果把问题化简一下,只求所有点对之间的路径之和,可以这样计算

#include <cstdio>
#include <vector>
#include <cstring>
#define ll long long
using namespace std;
const int maxn=1e4+10,maxm=500+10,mod=1e9+7;

int head[maxn],cnt;
struct Edge
{
    int nxt,to,w;
}edges[maxn<<1];
void add(int u,int v,int w)
{
    edges[++cnt].to=v;
    edges[cnt].w=w;
    edges[cnt].nxt=head[u];
    head[u]=cnt;
}
ll ans,dp[maxn],num[maxn];
void dfs(int u,int fa=-1)
{
    num[u]++;//初始化可以提出来
    for(int i=head[u];i!=-1;i=edges[i].nxt)
    {
        int v=edges[i].to,w=edges[i].w;
        if(v==fa) continue;
        dfs(v,u);
        ans=(ans+num[u]*num[v]*w+dp[u]*num[v]+dp[v]*num[u]);
        dp[u]+=dp[v]+w*num[v];
        num[u]+=num[v];
    }
}

int n;
int main()
{
    while(~scanf("%d",&n))
    {
        memset(dp,0,sizeof(dp));
        memset(num,0,sizeof(num));
        cnt=0,ans=0;
        for(int i=1;i<=n;++i) head[i]=-1;
        for(int i=1;i<=n-1;++i)
        {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            u++,v++;
            add(u,v,w);
            add(v,u,w);
        }
        dfs(1);
        printf("%lld\n",ans*2);
    }
    return 0;
}

暴力解法

#include <cstdio>
#include <vector>
#include <cstring>
#define ll long long
using namespace std;
const int maxn=5e4+10,maxm=500+10,inf=1e9;

int n,k;
vector<pair<int,int> > e[maxn];
ll ans[3];
int dis[maxn];

void dfs(int u,int fa=-1)
{
    for(auto x : e[u])
    {
        int v=x.first,w=x.second;
        if(v==fa) continue;
        dis[v]=dis[u]+w;
        dfs(v,u);
    }
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n-1;++i)
    {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        u++,v++;
        e[u].push_back({v,w});
        e[v].push_back({u,w});
    }
    for(int i=1;i<=n;++i)
    {
        memset(dis,0,sizeof(dis));
        dfs(i);
        for(int i=1;i<=n;++i)
            ans[dis[i]%3]+=dis[i];
    }
    printf("%lld %lld %lld\n",ans[0],ans[1],ans[2]);
    return 0;
}
  • 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、付费专栏及课程。

余额充值