題目描述: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