其实树形DP的思路还是基于DP,对于一棵树本来就有很好的递归性质,很多树形DP的问题,也不是不可以使用单纯的树上递归来解决,那为什么还要引入递归的问题呢??
很简单的回答就是,DP中的P就是programing 的意思,是一种记录,对于递归的低效性,很大一部分来源于对于单个问题的重复计算,我们使用一个数组(一维,或者二维)来记录我们已经求解得到的值,就可以大大提高算法的性能。比起直线性的DP,树形DP的特点还在于树形DP的遍历不是简单的从i到n的横扫形的遍历,树形的DP的遍历问题是依赖于DFS(深搜),比起直线形的遍历,树形DP的遍历,往往是从树的根部向叶子节点遍历,那么这个就利用到DFS的优势。
状态转移方法的实现:本题的状态转移方程还是很简单的 我们设置DP[Maxlen][2] 其中dp[i][1]代表的是编号为i的节点参与活动所获得的最大幸福值,dp[i][0]代表的是编号为i的节点不参与活动时所获得的最大幸福值。
那么我们可以推出状态转移方程为dp[i][1]=∑(j 1-n) dp[j][0]; dp[i][0]=∑(max(dp[j][1],dp[j][0])
那么实现DP过程依靠的是从根部到叶节点的DFS 每次DFS之后所求解的子问题就得以解决。最终返回值即可。
1 #include <iostream> 2 #include <algorithm> 3 #include <string.h> 4 using namespace std; 5 const int Maxlen=1e4; 6 int dp[Maxlen][2];//动态规划数组 7 //状态转移方程 dp[i][0]代表编号为i的节点不参与活动的最大开心值 8 // dp[i][1]代表编号为i的节点参与活动的最大开心值 9 // 显然ans=max(dp[n][1],dp[n][0]); 10 // 然后是状态转移方程 11 int parents[Maxlen],vis[Maxlen]; 12 int n,root; 13 void DFS(int u){ 14 int i; 15 for(i=1;i<=n;i++) 16 if(vis[i] == 0 && parents[i] == u){ 17 DFS(i); 18 dp[u][0]+=max(dp[i][1],dp[i][0]); 19 dp[u][1]+=dp[i][0]; 20 } 21 } 22 23 int main(){ 24 25 int i,j,l,k; 26 scanf("%d",&n); 27 memset(dp,0,sizeof(dp)); 28 for(i=1;i<=n;i++) 29 scanf("%d",&dp[i][1]); 30 memset(parents,0,sizeof(parents)); 31 while(~scanf("%d %d",&l,&k)){ 32 if(l == 0 && k == 0) 33 break; 34 parents[l]=k; 35 } 36 for(i=1;i<=n;i++) 37 if(parents[i] == 0) 38 root=i; 39 DFS(root); 40 printf("%d ",max(dp[root][0],dp[root][1])); 41 42 return 0; 43 }