树型dp

​ 树型 d p dp dp 即在树上进行 d p dp dp

​ 树是无环图,顺序可以是从叶子到根节点,也可以从根到叶子节点。

​ 一般树型 d p dp dp 的特征很明显,即状态可以表示为树中的节点,每个节点的状态可以由其子节点状态转移而来(从叶子到根的顺序),或是由其父亲节点转移而来(从根到叶节点的顺序),也可是两者结合。

​ 找出状态和状态转移方程仍然是树型 d p dp dp 的关键。

例题:蓝桥舞会

题目链接:蓝桥舞会 - 蓝桥云课 (lanqiao.cn)

图片描述

​ 思路:

​ 假设题目样例也变化为左图,那么便有又图的结果:

图片描述

​ 1.首先确定 d p dp dp 数组以及数组每一维表示的意义。 d p [ i ] [ j ] dp[ i ][ j ] dp[i][j] 表示以 i i i 为根节点的子树, j j j 的取值只有 0 0 0 1 1 1 1 1 1 表示选了节点 i i i 0 0 0 表示没选节点 i i i 。然后确定状态的转移,当选了 i i i 时,根据题目要求不能选择 i i i 的直接子节点,当没有选择 i i i 时,可以选择与 i i i 直接相连的子节点,当然,也可以不选。那么转移公式如下:

​ 2. d p [ i ] [ 0 ] + = m a x ( d p [ u ] [ 0 ] , d p [ u ] [ 1 ] ) dp[ i ][ 0 ] += max(dp[ u ][ 0 ] , dp[ u ][ 1 ]) dp[i][0]+=max(dp[u][0],dp[u][1]) 其中 u u u 是与 i i i 直接相连的子节点,对于该子节点,我既可以选,也可以不选,取两种状态的最大值。

​ 3. d p [ i ] [ 1 ] + = d p [ u ] [ 0 ] dp[ i ][ 1 ] += dp[ u ][ 0 ] dp[i][1]+=dp[u][0] 其中 u u u 是与 i i i 直接相连的子节点,对于该子节点,必然不能选择,只有一种状态。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.ArrayList;

public class Main {
    static ArrayList<Integer>[] q;//存储结点i的所有子结点
    static boolean[] visit;//判断此点是否是根节点
    static long[][] dp;
    static int[] a;//记录每一个人的快乐指数
    static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
    public static void main(String[] args) throws IOException {
        int n = Integer.parseInt(in.readLine());
        a = new int[n + 1];
        q = new ArrayList[n+1];
        dp = new long[n + 1][2]; 
        visit = new boolean[n + 1];
        String[] s = in.readLine().split(" ");
        for(int i=1;i<=n;i++) {
            a[i] = Integer.parseInt(s[i-1]);
            q[i] = new ArrayList<>();
        }
        for(int i=1;i<n;i++) {
            s = in.readLine().split(" ");//u的上司是v
            int u = Integer.parseInt(s[0]);
            int v = Integer.parseInt(s[1]);
            visit[u] = true;//u不是根结点
            q[v].add(u);//u是v的一个子结点(下属)
        }
        int root = -1;
        for(int i=1;i<=n;i++) {
            if(!visit[i]) {//只要根结点的visit值仍为false
                root = i;
                break;
            }
        }
        dfs(root);
        System.out.println(Math.max(dp[root][1], dp[root][0]));//最终结果考虑根结点和不考虑根结点的最大值
    }
    static void dfs(int root) {
        dp[root][1] += a[root];//考虑根结点的初值为根结点的快乐指数
        for (int i = 0; i < q[root].size(); i++) {//处理root的每一个子结点
            int to = q[root].get(i);
            dfs(to);
            dp[root][1] += dp[to][0];//考虑根结点,那么不能考虑子结点
            dp[root][0] += Math.max(dp[to][0], dp[to][1]);//不考虑根结点,子结点可以考虑,也可以不考虑
        }
    }
}
  • 13
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值