树形DP

275 阅读2分钟

树形DP

没有上司的舞会

  • 问题背景

Snipaste_2023-03-28_19-19-12.png

  • Dp分析

Snipaste_2023-03-28_19-18-05.png

Snipaste_2023-03-28_19-17-10.png

  • 状态表示

    • 两个二维状态表示f(u,0)f(u,1)
    • f(u,0)f(u,1)分别表示的是哪一个集合:所有满足如下条件的集合
      • f(u,0):所有从以u为根的子树中选择,并且不选u这个点的方案
      • f(u,1):所有从以u为根的子树中选择,并且选择u这个点的方案
    • f(u,0)f(u,1)存的是什么属性:MaxMin数量;在这里f(u,0)f(u,1)存的应该是数量,即从以u为根的子树中选择,并且选/不选u这个点的方案所能达到的快乐指数的最大值
  • 状态计算:f(u,0)f(u,1)可以怎么算出来?

    • f(u,0)
      • 不选择根节点,那么根节点的子节点可选可不选
      • f(u, 0) = Σ max( f(si, 0), f(si, 1) )siu 的每个子节点
    • f(u, 1)
      • 选择根节点,那么根节点的子节点一定不选
      • f(u, 1) = Σ f(si, 0)siu 的每个子节点
    • 这就是一个dfs的过程
  • 代码

    • public static void dfs(int u) {
          f[u][1] = happy[u];
          for (int i = h[u]; i != -1; i = ne[i]) {
              int j = e[i];
              dfs(j);
              f[u][0] += Math.max(f[j][0], f[j][1])
              f[u][1] += f[j][0];
          }
      }
      

练习

01 没有上司的舞会

  • 题目

Snipaste_2023-03-28_19-56-42.png

  • 题解
import java.io.*;
import java.util.*;

public class Main {
    public static final int N = 6010;
    public static int[] h = new int[N];
    public static int[] e = new int[N];
    public static int[] ne = new int[N];
    public static int[] happy = new int[N];
    //存当前点是否有父节点
    public static boolean[] st = new boolean[N];
    //一个节点的两个状态
    public static int[][] f = new int[N][2];
    public static int n, idx;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
        n = Integer.parseInt(br.readLine());
        for (int i = 1; i <= n; i++) {
            happy[i] = Integer.parseInt(br.readLine());
        }
        Arrays.fill(h, 1, n + 1, -1);
        Arrays.fill(st, 1, n + 1, false);
        while (n-- > 1) {
            String[] str1 = br.readLine().split(" ");
            int a = Integer.parseInt(str1[0]);
            int b = Integer.parseInt(str1[1]);
            add(b, a);
            st[a] = true;
        }
        //寻找根节点
        int u = 1;
        while (st[u]) {
            u++;
        }
        dfs(u);

        pw.println(Math.max(f[u][0], f[u][1]));
        pw.close();
        br.close();
    }

    public static void add(int a, int b) {
        e[idx] = b;
        ne[idx] = h[a];
        h[a] = idx++;
    }

    public static void dfs(int u) {
        f[u][1] = happy[u];
        for (int i = h[u]; i != -1; i = ne[i]) {
            int j = e[i];
            dfs(j);

            f[u][0] += Math.max(f[j][0], f[j][1]);
            f[u][1] += f[j][0];
        }

    }
}