题目描述:http://hihocoder.com/problemset/problem/1104
这道题用树形dp来解答,第一种方法容易想到,用dp[i][j]来表示以i为根节点的子树中恰好访问j个节点所获得的最大score,并且这j个节点中必须包含子树中所有必须访问到的节点。注意:如果一个节点是推荐节点,那么它的父节点也必须被访问。
第一层循环,计算出子树i中必须被访问的节点,并且用temp来记录访问这些必要节点所得到的score,且temp还包括节点i的score,这样,如果子树中没有推荐节点,temp就是i的score;然后dp[i][must[i]]=temp,若没有推荐节点,那么dp[i][1]=temp;dp[i][<must[i]]=-1,表示这些情况不可能,因为要访问全部必要节点,至少要访问must[i]个节点。
第二层循环就是通过枚举每一个子节点来计算在访问了必要节点的情况下访问j个节点得到的的最大score,注意,对于must[v]不为零的子节点,也要枚举,因为除了子树v中的必要节点外,还可以再访问其他节点,还有,求解时要减去dp[v][must[v]],在这里改了好多次,好在最后AC了。
#include <stdio.h>
#include <string.h>
#define MAX_N 100
struct EDGE{
int to, next;
}e[MAX_N*2];
//fe[]记录每个节点的第一个子节点,sco[]记录score,isRec[]记录是否recommended
//dp[i][j]表示以i为根节点的子树中恰好访问j个节点且rec节点全部访问所获得的最大score
int fe[MAX_N+1], n, m, k, sco[MAX_N+1], isRec[MAX_N+1], dp[MAX_N+1][MAX_N+1];
//must[i]记录以i为根节点的子树中必须访问的节点的个数
int must[MAX_N+1];
void Add(int a, int b, int eCount){
e[eCount].to = b;
e[eCount].next = fe[a];
fe[a] = eCount;
}
int max(int a, int b){
return a > b ? a : b;
}
void dfs(int u, int fa){
int i, v, temp, x, y, yy;
must[u] = isRec[u];
//计算以i为根节点的子树中必须访问的节点的个数,并计算temp=这些节点的score之和
temp = sco[u];
for(i = fe[u]; i+1; i = e[i].next){
v = e[i].to;
if(v != fa){
dfs(v, u);
if(must[v]){
must[u] += must[v];
temp += dp[v][must[v]];
}
}
}
if(!isRec[u] && must[u]){
must[u]++;
}
//如果有rec节点,则访问这些节点可获得相应的score,否则,temp为节点i的score
if(must[u]){
dp[u][must[u]] = temp;
}else{
dp[u][1] = temp;
}
for(i = fe[u]; i+1; i = e[i].next){
v = e[i].to;
if(v != fa){
for(x = m; x > must[u]; x--){
for(y = must[v]+1; y < x; y++){
if(dp[u][x-y+must[v]] != -1 && dp[v][y] != -1){
if(must[v]) temp = dp[v][must[v]];
else temp = 0;
dp[u][x] = max(dp[u][x], dp[u][x-y+must[v]]+dp[v][y]-temp);
}
}
}
}
}
}
int main(){
int i, a, b, r, eCount;
scanf("%d%d%d", &n, &k, &m);
for(i = 1; i <= n; i++){
scanf("%d", &sco[i]);
}
memset(isRec, 0, sizeof(isRec));
for(i = 0 ;i < k; i++){
scanf("%d", &r);
isRec[r] = 1;
}
memset(fe, -1, sizeof(fe));
for(eCount = 0, i = 1; i < n; i++){
scanf("%d%d", &a, &b);
Add(a, b, eCount);
eCount++;
Add(b, a, eCount);
eCount++;
}
memset(dp, -1, sizeof(dp));
dfs(1, -1);
printf("%d\n", dp[1][m]);
return 0;
}
第二种方法比较机智
参见这篇文章:http://blog.csdn.net/wsjingping/article/details/45889615
其中有一个错误的地方,就是判断是否有解的时候,不能简单的比较k<=m,因为要访问推荐节点,还必须访问它的父节点,所以应判断dp[1][m]是否小于等于零,然则输出-1