一道基础的树形DP,为我们基本讲解出树形DP的特点。
按照我浅薄的理解,树形DP是把寻找最优算法放到树上进行,先是找到子根,然后往上递推回去,需要记录每个点的父根。
这道题中我采取的方法是用数组记录一个节点的父根,也可以用vector,vector比较适用于记录自己的多子根,输入的时候进行处理,i对应的father[i]的值就是其父根。因为初始化为0所以最后值为0的i就是整棵树的父根。
接下来就找到整棵树的父根,然后开始深搜,搜到最后的子根及其对应的父根,对于其父根而言,可以选择去或不去,去的话子根就不能去,不去的话就选择子根去或不去的最大值。
这里就引出了最重要的状态转移方程,因为这道题里每个根只有去或不去两种可能,因此设置dp[i][j],i是对应的根,j只有两个值0或1,0代表不去,1代表去,值自然就是这种情况下的最优解(即最多人去的)。从下往上递归,因此状态转换方程是:
dp[node][1]+=dp[i][0];
dp[node][0]+=std::max(dp[i][0],dp[i][1]);
意思就是假如父根去了,只能选择不去的子根;父根不去,就选择去或不去最大的子根情况。然后一路推上去。
/*
* POJ2342AnniversaryParty.cpp
*
* Created on: 2014年7月8日
* Author: Prophet
*/
#include<cstdio>
#include<string.h>
#include<algorithm>
const int maxn = 6005;
int dp[maxn][2];
int father[maxn];//i对应的元素是i的父根
bool visit[maxn];//记录是否询问过
int n;
void dfs(int node);
int main(){
while(scanf("%d",&n)!=EOF){
for(int i=1;i<=n;i++)
scanf("%d",&dp[i][1]);
int l,k;
int root=0;//整棵树的父根
memset(father,0,sizeof(father));
while(scanf("%d%d",&l,&k),l+k>0){
father[l]=k;
}
for(int i=1;i<=n;i++){//找出整棵树的父根
if(!father[i]){
root = i;
break;
}
}
memset(visit,false,sizeof(visit));
dfs(root);
printf("%d\n",std::max(dp[root][1],dp[root][0]));
}
}
void dfs(int node){
visit[node]=true;
for(int i=1;i<=n;i++){
if(!visit[i]&&father[i]==node){//如果该点没被以之为父根且其父根为node,即node的子树且还没到过
dfs(i);//一直到找到最后一个节点(i)及其父亲(node)
dp[node][1]+=dp[i][0];
dp[node][0]+=std::max(dp[i][0],dp[i][1]);
}
}
}