一、题目
题目描述 Description
Ural大学有N个职员,编号为1~N。他们有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的直接上司。每个职员有一个快乐指数。现在有个周年庆宴会,要求与会职员的快乐指数最大。但是,没有职员愿和直接上司一起与会。
输入描述 Input Description
第一行一个整数N。(1<=N<=6000)
接下来N行,第i+1行表示i号职员的快乐指数Ri。(-128<=Ri<=127)
接下来N-1行,每行输入一对整数L,K。表示K是L的直接上司。
最后一行输入0,0。
输出描述 Output Description
输出最大的快乐指数。
样例输入 Sample Input
7
1
1
1
1
1
1
1
1 3
2 3
6 4
7 4
4 5
3 5
0 0
样例输出 Sample Output
5
二、分析
1、通过题意可以知道公司职员的从属关系可以形成一棵关系树,这一题是典型的树状动态分析。
2、假设有下面这样一棵树。
节点A是节点B,C,D的父节点(直接上司)
每个职员都有去与不去两种状态,且当直接上司去的时候,其直接下属不去。
由上可设dp[A][0]为A职员不去时的快乐值,dp[A][1]为A职员去时的快乐值,
那么dp[A][1]=dp[B][0]+dp[C][0]+dp[D][0]+happy[A],(本身的快乐值)
,而dp[A][0]=max{dp[B][0],dp[B][1]}+max{dp[C][0],dp[C][1]}+max{dp[D][0],dp[D][1]},
(因为直接上司不去的时候,其直接下属可选择去与不去,因为题目要求宴会快乐的最大值,所以计算每直接上级节点时都要比较其直接下属去与不去时的快乐值,选择最大的一个)
以上就是该题动态规划方程的推导过程。
如果A是这个一棵职员关系树的根节点,那么max{dp[A][0],dp[A][1]}便是这个树的最大值,也就是与会职员的快乐最大值
public class Demo7 {
// 静态嵌套类
static class Node {
Vector<Integer> vector = new Vector<>();// 下属集合
int boss = 0;//直接上司的下标
int[] dp = new int[2];// 0--->不去 1--->去
int happy = 0;//自身的快乐值
}
static Node[] nodes;
public static void getResult(int index) {
int i;
for (i = 0; i < nodes[index].vector.size(); i++) {
int x = nodes[index].vector.get(i);// x是下属
//先计算直接下属
getResult(x);
// 根据状态方程
nodes[index].dp[0] += Math.max(nodes[x].dp[0], nodes[x].dp[1]);
nodes[index].dp[1] += nodes[x].dp[0];
}
// 去的情况下,处理完所有子树最大取值之后还要加上自己的快乐值
nodes[index].dp[1] += nodes[index].happy;
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = Integer.parseInt(scanner.nextLine());
String s[] = new String[2];
String string = null;
nodes = new Node[n + 1];
for (int i = 1; i <= n; i++) {
nodes[i] = new Node();
nodes[i].happy = Integer.parseInt(scanner.nextLine());
}
for (int i = 1; i < n; i++) {
s = scanner.nextLine().split(" ");
int sub = Integer.parseInt(s[0]);
int boss = Integer.parseInt(s[1]);
nodes[boss].vector.add(sub);
nodes[sub].boss = boss;
}
/*
* for (int i = 1; i < n; i++) { System.out.print("第" + i + "个职员的幸福指数:"
* + nodes[i].happy + " 上司:" + nodes[i].boss + " "); for (int j = 0; j <
* nodes[i].vector.size(); j++) {
* System.out.print("下属"+nodes[i].vector.get(j)); } }
*/
for (int i = 1; i <= n; i++) {
//从根节点开始,向下搜索
if (nodes[i].boss == 0) {
// System.out.println(i);
getResult(i);
//取最大值
System.out.println(Math.max(nodes[i].dp[1], nodes[i].dp[0]));
break;
}
}
}
}
1、上面代码中通过静态嵌套类来表示关系树中的一个节点,然后创建了一个Node类型的数组来表示关系树。
2、在计算得出root节点的dp[1]和dp[0]后,还要进行比较才行,因为题目要求的是求快乐的最大值,且一个职员只能是去或者不去两种情况。
Whanever you have an aim you must sacrifice something of freedom to attain it.