树形DP
没有上司的舞会
- 问题背景
- Dp分析
-
状态表示
- 两个二维状态表示
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)存的是什么属性:Max,Min,数量;在这里f(u,0)和f(u,1)存的应该是数量,即从以u为根的子树中选择,并且选/不选u这个点的方案所能达到的快乐指数的最大值
- 两个二维状态表示
-
状态计算:
f(u,0)和f(u,1)可以怎么算出来?f(u,0)- 不选择根节点,那么根节点的子节点可选可不选
- f(u, 0) = Σ max( f(s
i, 0), f(si, 1) ),si为 u 的每个子节点
f(u, 1)- 选择根节点,那么根节点的子节点一定不选
- f(u, 1) = Σ f(s
i, 0),si为 u 的每个子节点
- 这就是一个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 没有上司的舞会
- 题目
- 题解
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];
}
}
}