算法分析
树形dp
状态表示
-
f[u][0]
:所有以u
为根的子树中选择,并且不选u
这个点的方案 -
f[u][1]
:所有以u
为根的子树中选择,并且选u
这个点的方案 -
属性:
Max
状态计算
当前u
结点不选,子结点可选可不选
- $f[u][0] = \sum max(f[s_i,0],f[s_i,1])$
当前u
结点选,子结点一定不能选
- $f[u][1] = \sum (f[s_i,0])$
时间复杂度 $O(n)$
Java 代码
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Scanner;
import java.util.Set;
public class Main{
static int N = 6010;
static int[] happy = new int[N];
static int[] h = new int[N];
static int[] e = new int[N];
static int[] ne = new int[N];
static int idx = 0;
static int[][] f = new int[N][2];
static boolean[] has_fa = new boolean[N];
static void add(int a,int b)
{
e[idx] = b;
ne[idx] = h[a];
h[a] = idx ++;
}
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][1] += f[j][0];
f[u][0] += Math.max(f[j][1], f[j][0]);
}
}
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
for(int i = 1;i <= n;i ++) happy[i] = scan.nextInt();
Arrays.fill(h, -1);
for(int i = 0;i < n - 1;i ++)
{
int a = scan.nextInt();
int b = scan.nextInt();
add(b,a);
has_fa[a] = true;
}
int root = 1;
while(has_fa[root]) root ++;
dfs(root);
System.out.println(Math.max(f[root][0], f[root][1]));
}
}
小呆呆牛皮
请教下, 这个函数怎么理解啊?
static void add(int a,int b)
{
e[idx] = b;
ne[idx] = h[a];
h[a] = idx ++;
}
这是一个建图操作,具体看
https://www.acwing.com/solution/content/5051/
多谢!~我说Y总怎么一带而过呢。原来大家都学过了。hh
小呆呆,题解真好
谢谢hh
为什么这个dfs可以不用st数组标记是否走过
不需要,本质上是一个dp问题,从一个集合的角度分析
我觉得不是这个原因吧,由于这个图是树,没有环,而且是有向的,从上向下遍历的,不会向上走
是的
也可以理解成只有有向边,不会逆向,如果是双向边,也可以在dfs函数中,加上父节点的变量,有同样的效果