java---树形dp---没有上司的舞会(每日一道算法2022.10.24)

这题看着很花哨,实际很简单的,慢慢读题即可

题目:
Ural 大学有 N 名职员,编号为 1∼N
他们的关系就像一棵以校长为根的树,父节点就是子节点的直接上司
每个职员有一个快乐指数,用整数 Hi 给出,其中 1 ≤ i≤ N
现在要召开一场周年庆宴会,不过,没有职员愿意和直接上司一起参会
在满足这个条件的前提下,主办方希望邀请一部分职员参会,使得所有参会职员的快乐指数总和最大,求这个最大值

第一行一个整数 N
接下来 N 行,第 i 行表示 i 号职员的快乐指数 Hi
接下来 N−1 行,每行输入一对整数 L,K,表示 K 是 L 的直接上司

输出最大的快乐指数

1 ≤ N ≤ 6000
−128 ≤ Hi ≤ 127

输入:
7
1
1
1
1
1
1
1
1 3
2 3
6 4
7 4
4 5
3 5
输出:
5
public class 树形dp_没有上司的舞会 {
    //树以邻接表的形式存储到h/ne/e
    //由于题目中没有告诉我们根节点root是哪个点,那么只能用has_father记录一下所有有父节点的节点,那么false的就是root
    //happy记录每个点的快乐值
    //f表示状态,f[u][0]为以u为根节点,不选u节点的方案,f[u][1]为以u为根节点,选u节点的方案
    public static int N = 6010, n, index = 0;
    public static int[] h = new int[N], ne = new int[N], e = new int[N];
    public static int[] happy = new int[N];
    public static boolean[] has_father = new boolean[N];
    public static int[][] f = new int[N][2];

    public static void main(String[] args) {
        //读入快乐值
        Scanner in = new Scanner(System.in);
        n = in.nextInt();
        for (int i = 1; i<=n; i++) happy[i] = in.nextInt();

        //读入所有点之间的连接,记得将头节点初始化为-1,要不然dfs会爆的哦~
        Arrays.fill(h, -1);
        for (int i = 0; i<n-1; i++) {
            int a = in.nextInt(), b = in.nextInt();
            insert(b, a);
            has_father[a] = true;
        }

        //找到根节点root
        int root = 1;
        while (has_father[root]) root++;	//如果has_father为false,说明就是根节点

        dfs(root); //从根节点开始dfs更新状态

        //最后选择选root还是不选root,即为答案
        System.out.println(Math.max(f[root][0], f[root][1]));	
    }

    //模拟在单链表头插入,相当于x单向连接y
    public static void insert(int x, int y) {
        e[index] = y;
        ne[index] = h[x];
        h[x] = index++;
    }

    //通过深搜dfs的形式更新状态
    public static void dfs(int u) {
        f[u][1] = happy[u];  //选择u,那么状态先加上u自己的快乐值

        //很基础的邻接表遍历,遍历所有当前u点的子节点j,先将所有底部节点更新状态,再用子节点来更新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];
        }
    }
}

思路:
终于有个相对简单的题了(哭),还是经典y式dp法

1.状态表示
f[u][0] :以u为根节点,不选u节点的方案
f[u][1] :以u为根节点,选u节点的方案
存储的值为Max,因为要找最大的快乐指数(快乐值)

2.状态计算
1.f[u][0], 不选u点
说明可以选择u点的子节点 j,或者不选u点的子节点 j,取这两种状态的max即可 (当然必须要计算所有u的子节点才可以)
也就是 f[u][0] += Max(f[j][0], f[j][1])

2.f[u][1], 选择u点
说明不能选u的子节点 j,因为下属不想和直系上司呆在一块
也就是只有一种情况:f[u][1] += f[j][0]

以题目为栗:
所有人的快乐值都是1,那么只也就是考虑如果能选到更多的子节点,自然就得到更多的快乐值
不选5的话,只能选3和4,快乐值就是1+1 = 2
选5的话,可以选5,1,2,6,7,快乐值就是1+1+1+1+1 = 5
(切记这题的重点就是,不能选已选点的子节点)
请添加图片描述

声明:
算法思路来源为y总,详细请见https://www.acwing.com/
本文仅用作学习记录和交流

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值