P1352 没有上司的舞会
解题思路
这是一道经典的树形动态规划问题。我们需要在一棵树中选择一些节点(职员),使得:
-
不能同时选择一个节点和它的直接父节点(上司和下属不能同时参加)
-
选择的节点的权值(快乐指数)之和最大
动态规划的状态设计:
-
dp[u][0]表示不选择节点 u 时,以 u 为根的子树能获得的最大快乐值 -
dp[u][1]表示选择节点 u 时,以 u 为根的子树能获得的最大快乐值
状态转移方程:
-
当不选择 u 时,它的子节点可以选择或不选择:
dp[u][0] = Σ max(dp[v][0], dp[v][1])(v 是 u 的子节点) -
当选择 u 时,它的子节点不能被选择:
dp[u][1] = a[u] + Σ dp[v][0](v 是 u 的子节点)
最终结果是 max(dp[root][0], dp[root][1])
代码注释
#include<bits/stdc++.h> using namespace std; const int N = 1e5 + 10, inf = 0x3f3f3f3f; vector<int> g[N]; // 邻接表存储树结构 int f[N]; // 记录每个节点的父节点 int a[N]; // 存储每个节点的快乐指数 int dp[N][2]; // dp数组,dp[u][0]表示不选u时的最大值,dp[u][1]表示选u时的最大值 int n, rt, ans = -1e9; // n是节点数,rt是根节点,ans存储最终答案 void dfs(int x) { dp[x][1] = a[x]; // 初始化:选择x时的快乐值至少是x本身的快乐值 int sum1 = 0, sum2 = 0; // sum1记录选子节点的和,sum2记录不选子节点的和 for(int i = 0; i < g[x].size(); i++) { int y = g[x][i]; // 遍历x的所有子节点y if(y == f[x]) continue; // 避免回溯到父节点 dfs(y); // 递归处理子节点 // 累加子节点选或不选的情况 sum1 += dp[y][1]; // 所有子节点都选的情况(实际不会这样用) sum2 += dp[y][0]; // 所有子节点都不选的情况 // 正确的状态转移: // 如果选x,那么子节点y不能选,所以加上dp[y][0] dp[x][1] += dp[y][0]; // 如果不选x,那么子节点y可选可不选,取最大值 dp[x][0] += max(dp[y][0], dp[y][1]); // 原代码中这一行被注释掉了,其实不需要在dfs过程中更新ans // ans = max(ans,max(dp[x][0],dp[x][1])); } } int main() { cin >> n; // 读取每个节点的快乐指数 for(int i = 1; i <= n; i++) cin >> a[i]; // 建立树结构,注意题目中第一个数是下属,第二个数是上司 for(int i = 1; i < n; i++) { int x, y; cin >> x >> y; g[y].push_back(x); // y是x的上司,所以x是y的子节点 f[x] = y; // 记录x的父节点是y } // 寻找根节点(没有父节点的节点) for(int i = 1; i <= n; i++) if(f[i] == 0) rt = i; // 从根节点开始进行DFS遍历 dfs(rt); // 遍历所有节点,找出最大值(其实只需要看根节点即可) for(int i = 1; i <= n; i++) ans = max(ans, max(dp[i][0], dp[i][1])); cout << ans; return 0; }

浙公网安备 33010602011771号