1. 题目
题目链接:P1352「没有上司的舞会」 。
题目描述
某大学有 n 个职员,编号为 1…n 。
他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的直接上司。
现在有个周年庆宴会,宴会每邀请来一个职员都会增加一定的快乐指数 ri,但是呢,如果某个职员的直接上司来参加舞会了,那么这个职员就无论如何也不肯来参加舞会了。
所以,请你编程计算,邀请哪些职员可以使快乐指数最大,求最大的快乐指数。
输入格式
输入的第一行是一个整数 n。
第 2 到第 (n+1) 行,每行一个整数,第 (i+1) 行的整数表示 i 号职员的快乐指数 ri 。
第 (n+2) 到第 2n 行,每行输入一对整数 l,k,代表 k 是 l 的直接上司。
输出格式
输出一行一个整数代表最大的快乐指数。
输入输出样例
输入 #1
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 7 1 1 1 1 1 1 1 1 3 2 3 6 4 7 4 4 5 3 5
|
输出 #1
2. 题解
分析
这是一道简单的树上 dp 问题:
- 上司不参加舞会时,下属可参加可不参加:f[i][0]=∑maxf[x][1],f[x][0]
- 上司参加舞会时,下属都不会参加:f[i][1]=∑f[x][0]+ai
然后具体实现计算时:
- 可以采用 DFS 遍历整棵树,在返回上一层时更新当前结点的最优解。
- 或者可以利用树的性质,即每个非根结点有且仅有一个父结点,因此可以先计算所有子树的最优解,再往上更新父结点。最开始先计算所有叶结点,即没有孩子结点的点,我们可以把孩子结点到父结点的边看作时父结点的入边,则叶结点就是入度为 0 的点,从而得到计算的结点的顺序即为结点拓扑排序的顺序。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| #include <bits/stdc++.h> #define ll int #define MAXN 6005 using namespace std;
ll cnt; ll head[MAXN]; struct edge{ ll to; ll next; } e[MAXN]; void init(ll n) { cnt = 0; memset(head, -1, sizeof(head)); } void addEdge(ll u, ll v) { e[cnt].to = v; e[cnt].next = head[u]; head[u] = cnt++; }
ll in[MAXN]; ll parent[MAXN]; ll dp[MAXN][2];
int main() { ll n; ll a[MAXN]; scanf("%d", &n); for(ll i = 1; i <= n; ++i) { scanf("%d", a+i); } ll u, v; init(n); for(ll i = 0; i < n-1; ++i) { scanf("%d%d", &u, &v); parent[u] = v; addEdge(v, u); ++in[v]; } queue <ll> q; for(ll i = 1; i <= n; ++i) { if(!in[i]) { dp[i][1] = a[i]; --in[parent[i]]; if(!in[parent[i]]) { q.push(parent[i]); } } } while(q.size()) { u = q.front(); q.pop(); --in[parent[u]]; if(!in[parent[u]]) q.push(parent[u]); dp[u][0] = dp[u][1] = 0; for(ll i = head[u]; i != -1; i = e[i].next) { dp[u][0] += max(dp[e[i].to][0], dp[e[i].to][1]); dp[u][1] += dp[e[i].to][0]; } dp[u][1] += a[u]; } printf("%d\n", max(dp[u][0], dp[u][1])); return 0; }
|