每日算法:没有上司的舞会

112 阅读3分钟

题目本体

题目名称:没有上司的误会

题源:洛谷P1352

某大学有 nn 个职员,编号为 11 到 nn。他们之间有从属关系,形成一棵以校长为根的树。现在有个周年庆宴会,每邀请一个职员都会增加一定的快乐指数 riri​,但如果某个职员的直接上司来参加舞会了,那么这个职员就无论如何也不肯来参加舞会了。请你计算,邀请哪些职员可以使快乐指数最大,并求出最大的快乐指数。

输入格式:

  • 第一行是一个整数 nn。
  • 第 2 到第 (n+1)(n+1) 行,每行一个整数,第 (i+1)(i+1) 行的整数表示 ii 号职员的快乐指数 riri​。
  • 第 (n+2)(n+2) 到第 2n2n 行,每行输入一对整数 l,kl,k,代表 kk 是 ll 的直接上司。

输出格式:

输出一行一个整数代表最大的快乐指数。

示例:

plaintext
输入:
7
1
1
1
1
1
1
1
1 3
2 3
6 4
7 4
4 5
3 5

输出:
5

限制条件:

  • 对于 100% 的数据,保证 1≤n≤6×1031≤n≤6×103,−128≤ri≤127−128≤ri​≤127,1≤l,k≤n1≤l,k≤n,且给出的关系一定是一棵树。

题目思路

考虑这道题目,每个职员的决策会影响其上司和下属。我们可以只关注单向影响,即上司对下属的影响,因为这种影响是相互的。

状态定义

定义状态 f[i]:f[i] 表示第 ii 个人的位置能获得的最大快乐指数。然而,由于选择具有后效性,即一个职员的选择会影响其下属,仅用 f[i] 是不够的。

解决方案

为了解决后效性问题,我们需要增加一维状态来记录所有可能的情况。因此,状态定义为 dp[i][j],其中 i 表示职员,j 表示决策(0表示不参加,1表示参加)。

状态转移方程

  1. 不参加的情况:dp[i][0]=∑(max⁡(dp[son][1],dp[son][0]))dp[i][0]=∑(max(dp[son][1],dp[son][0]))

    • 如果职员 i 不参加,其下属可以自由选择是否参加。
  2. 参加的情况:dp[i][1]=∑(dp[son][0])+happy[i]dp[i][1]=∑(dp[son][0])+happy[i]

    • 如果职员 i 参加,其下属都不能参加,只能考虑其他下属不参加的情况。

关键点分析:

  • 树的存储,采用链式前向星构建和遍历树状数组,关于链式前向行可自行CSDN了解。
  • 状态定义:对于每个节点,定义两个状态,dp[i][0] 表示不邀请节点 i 时的最大快乐指数,dp[i][1] 表示邀请节点 i 时的最大快乐指数。
  • 状态转移:对于每个节点,我们需要根据其子节点的状态来更新自己的状态。

代码详解

cpp
#include<bits/stdc++.h>
using namespace std;
const int N = 6004;
int u, v, cnt, dp[N][2], rt, n, V[N], hd[N];
bool fa[N];
struct edge {
    int to, nx;
} e[N];

void add(int &u, int &v) {
    e[++cnt].to = v;
    e[cnt].nx = hd[u];
    hd[u] = cnt; 
}

void dfs(int rt) {
    dp[rt][0] = 0;
    dp[rt][1] = V[rt];
    
    for(int i = hd[rt]; i; i = e[i].nx) {
        int to = e[i].to;
        dfs(to);
        dp[rt][1] += dp[to][0];
        dp[rt][0] += max(dp[to][0], dp[to][1]);
    }
}

int main() {
    cin >> n;
    for(int i = 1; i <= n; i++) {
        cin >> V[i];
    }
    for(int i = 1; i < n; i++) {
        cin >> v >> u;
        add(u, v);
        fa[v] = true;
    }
    for(int i = 1; i <= n; i++) {
        if(!fa[i]) {
            rt = i;
            break;
        }
    }
    dfs(rt);
    cout << max(dp[rt][0], dp[rt][1]);
    return 0;
}