【动态规划】树形dp

参考文章:

  • 树形dp讲解(你不会后悔点进来)
  • 动态规划进阶(六):树形DP原理详解

核心思想:DFS遍历 +记忆化

  • 自底向上,后序遍历,父节点最优解从子节点转移过来

状态
节点维度:dp[u][s] 表示节点u在状态s下的最优解
常见状态:

  • 选择/不选当前节点
  • 颜色标记(如红黑树着色问题)
  • 距离限制(如树的直径)

典:没有上司的舞会

父节点最优解从子节点转移过来

  • 结构:领导下属的关系类似树
  • 状态:一个节点有两种状态,要么去要么不去
  • 转移:
    • 父节点去,子节点一定不去
    • 父节点不去,子节点可以去也可以不去
    • 转移方程
      • d p [ f ] [ 0 ] + = m a x ( d p [ s ] [ 0 ] , d p [ s ] [ 1 ] ) dp[f][0]+=max(dp[s][0],dp[s][1]) dp[f][0]+=max(dp[s][0],dp[s][1])
      • d p [ f ] [ 1 ] + = d p [ s ] [ 0 ] dp[f][1]+=dp[s][0] dp[f][1]+=dp[s][0]
const int N = 6e3+10;
int la[N],tr[N],nxt[N];//记录树
int head[N];//记录是否为根节点
int hp[N];//快乐指数
int cnt=1;
void add(int f,int s){//链式前向星 记录树结构
    tr[cnt]=s;
    nxt[cnt]=la[f];
    la[f]=cnt;
    cnt++;
}
int sum[N][2];//记录最优解
void dp(int root){//递归遍历树
    sum[root][0]=0;//不去
    sum[root][1]=hp[root];//去
    for(int i=la[root];i>=1;i=nxt[i]){
        int s=tr[i];//找儿子的值
        dp(s);//dfs一个儿子走到黑 自底向上
        sum[root][0]+=max(sum[s][1],sum[s][0]);
        sum[root][1]+=sum[s][0];
    }
}
void solve(){
    int n;cin>>n;
    forr(i,1,n){
        cin>>hp[i];
    }
    forr(i,1,n-1){
        int f,s;cin>>s>>f;
        add(f,s);
        head[s]=1;
    }
    int hd;
    forr(i,1,n)if(head[i]==0){hd=i;break;}
    // cout<
    dp(hd);
    // forr(i,1,n)cout<
    cout<<max(sum[hd][0],sum[hd][1])<<endl;
}

你可能感兴趣的:(动态规划,动态规划,算法)