题目传送门
最近做的题真是越来越水了(以前没有自己做过树形 DP 题,所以特来水一篇博客)
好了,进入正题。
思路
上司和下属的关系就是一种树形关系,上下级共同构成了一棵树,我们要考虑在这棵树里进行 DP。
由于上司对下属是有影响的(即上司选了下属就不能选了),这是有后效性的吗,所以需要从下属到上司进行 DP,因为下属的选择不会影响到上司的选择。(输入方式已经暗示了一切,因为先输入下属再输入其上司)
用一个二维数组表示状态。dp[v][0/1]
表示到节点 v 所获得的最大快乐指数,0/1 表示是否选择当前点 v。
易推出状态转移方程为 d p [ v ] [ 0 ] = ∑ max ( d p [ u ] [ 0 ] , d p [ u ] [ 1 ] ) dp[v][0] = \sum\max(dp[u][0],dp[u][1]) d p [ v ] [ 0 ] = ∑ max ( d p [ u ] [ 0 ] , d p [ u ] [ 1 ])
d p [ v ] [ 1 ] = ∑ d p [ u ] [ 0 ] + r [ v ] dp[v][1] = \sum dp[u][0] + r[v] d p [ v ] [ 1 ] = ∑ d p [ u ] [ 0 ] + r [ v ] (其中 u 是 v 的下属)
这两个方程也很好理解,即上司不去选择下属去不去的快乐指数最大值;上司去时下属不能去,所以只加上上司自己的快乐指数。
最后取所有 dp
数组中的数的最大值就可以了。
写代码时需要从底层向上递推,最底层的点是没有入度的,所以可以维护一个队列,每次加入没有入度的点,枚举其上司,而且在枚举到这个点的子节点时让入度减 1(相当于它的下属已经枚举过了,上司入度要减去 1)
代码
#include <bits/stdc++.h>
using namespace std ;
const int maxN = 6005 ;
struct Edge {
int next, to;
} edge[maxN << 1 ];
int head[maxN], ha[maxN], indeg[maxN];
int dp[maxN][ 2 ];
int cnt, n, ans;
queue <int> que;
void add ( int from , int to ) {
edge[ ++ cnt].next = head[from];
edge[cnt].to = to;
head[from] = cnt;
}
int main () {
memset (head, - 1 , sizeof (head));
scanf ( " %d " , & n);
for ( int i = 1 ; i <= n; i ++ ) scanf ( " %d " , & ha[i]);
for ( int i = 1 ; i < n; i ++ ) {
int a, b;
scanf ( " %d%d " , & a, & b);
add (a, b);
indeg[b] ++ ;
}
for ( int i = 1 ; i <= n; i ++ ) {
dp[i][ 1 ] = ha[i]; // 直接把快乐指数赋值给 dp[i][1] 了
if ( ! indeg[i]) que. push (i);
}
while ( ! que. empty ()) {
int u = que. front ();
que. pop ();
for ( int i = head[u]; ~ i; i = edge[i].next) {
int v = edge[i].to;
dp[v][ 1 ] += dp[u][ 0 ];
dp[v][ 0 ] += max (dp[u][ 0 ], dp[u][ 1 ]);
indeg[v] -- ;
if ( ! indeg[v]) que. push (v);
}
}
for ( int i = 1 ; i <= n; i ++ )
ans = max (ans, max (dp[i][ 0 ], dp[i][ 1 ]));
printf ( " %d\n " , ans);
return 0 ;
}