本文针对POJ上的2057
、1947
、2486
、3140
四道题目,谈一下个人对树形DP的理解,上面4题无法覆盖树形DP的所有形式,但也有一定的针对性,值得去做一做,思考一下:
树形DP中常常用到“背包思想”,1947和2486就是利用了“背包思想”,状态想好了,最后就是在填“背包”,某个节点的“背包”是用他们的子树的状态进行填充的。
- POJ1947:状态dp[i][p]表示以节点i为根的树,持有p个节点需要摧毁的最少的公路,状态转移方程大体可以写为dp[i][p]=Min{dp[i][p],{dp[i][p-k]+dp[j][k]|j is a child of i}}
- POJ2486:状态dp[i][k][0]表示有k步可以走的情况下,女主角回到i节点吃到的最大苹果数;dp[i][k][1]表示有k步可以周的情况下,女主角不一定回到i(可能在i的子树中停止)吃到的最大苹果数。状态转移方程大体可以写为三个(1)dp[i][k][0]=Max{dp[i][k][0],{dp[i][k-l-2][0]+dp[j][l][0]|j is a child of i}};(2)dp[i][k][1]=Max{dp[i][k][1],{dp[i][k-l-2][0]+dp[j][l][1]|j is a child of i}};(3)dp[i][k][1]=Max{dp[i][k][1],{dp[i][k-l-2][1]+dp[j][l][0]|j is a child of i}};
至于POJ3140,就是一个水题
POJ1947 codes
#include<iostream>
#include<string.h>
using namespace std;
#define SIZE 152
int dp[SIZE][SIZE];
int children[SIZE][SIZE];
int parent[SIZE];
int col[SIZE];
int P;
void dfs(int now){
if(children[now][0]==0){
dp[now][1]=0;
return ;
}
dp[now][1]=0;
for(int i=1;i<=children[now][0];i++){
dfs(children[now][i]);
for(int j=P;j>=1;j--){
if(dp[now][j]!=-1){
for(int k =1;k<=P;k++)
if(dp[children[now][i]][k]!=-1 && j+k<=P &&
(dp[now][j+k]==-1 || dp[children[now][i]][k]+dp[now][j] < dp[now][j+k])){
dp[now][j+k] = dp[children[now][i]][k]+dp[now][j];
}
dp[now][j]+=1;
}
}
}
}
int main()
{
int N;
scanf("%d%d",&N,&P);
memset(dp,-1,sizeof(dp));
memset(children,0,sizeof(children));
memset(parent,-1,sizeof(parent));
memset(col,-1,sizeof(col));
for(int i=1;i<N;i++){
int p,c;
scanf("%d%d",&p,&c);
parent[c]=p;
children[p][++children[p][0]] = c;
}
int root = 0;
for(int i=1;i<=N;i++)
if(parent[i]==-1){
root = i;
break;
}
dfs(root);
int res = -1;
for(int i=1;i<=N;i++)
if(dp[i][P]!=-1){
int tmp = 0;
if(parent[i]==-1) tmp = dp[i][P];
else tmp = dp[i][P]+1;
if(res==-1 || tmp < res)
res = tmp;
}
printf("%d\n",res);
system("pause");
}
POJ2486 codes
/*
dp[i][j][0]:在i节点还可以走j步,又走回i节点得到的最大苹果数
dp[i][j][1]:在i节点还可以走j步,不一定走回i节点得到的最大苹果数
dp[i][j][0]的转移方程很简单,dp[i][j][1]的转移方程有两个,注意体会第二
*/
#include<iostream>
#include<string.h>
using namespace std;
#define SIZE 105
int children[SIZE][SIZE];
int val[SIZE];
int dp[SIZE][205][2];
int N,K;
#define _max(x,y) ((x)>(y)?(x):(y))
void dfs(int now,int parent){
for(int i=0;i<=K;i++)
dp[now][i][0]=dp[now][i][1]=val[now];
if(children[now][0]!=0){
for(int i=1;i<=children[now][0];i++){
int child = children[now][i];
if(child == parent) continue;
dfs(child,now);
for(int k=K;k>=0;k--)
for(int j=0;j<=k;j++){
//dp[now][k+2][0]
dp[now][k+2][0] = _max(dp[now][k+2][0],dp[now][k-j][0]+dp[child][j][0]);
//dp[now][k+1][1]
dp[now][k+1][1] = _max(dp[now][k+1][1],dp[now][k-j][0]+dp[child][j][1]);
//dp[now][k+2][1]
dp[now][k+2][1] = _max(dp[now][k+2][1],dp[now][k-j][1]+dp[child][j][0]);
}
}
}
}
int main()
{
while(scanf("%d%d",&N,&K)!=EOF){
for(int i=1;i<=N;i++)
scanf("%d",&val[i]);
memset(children,0,sizeof(children));
for(int i=1;i<N;i++){
int from,to;
scanf("%d%d",&from,&to);
children[from][++children[from][0]] = to;
children[to][++children[to][0]] = from;
}
memset(dp,0,sizeof(dp));
dfs(1,-1);
printf("%d\n",dp[1][K][1]);
}
}