概念
树型DP即在树上进行DP。树是无环图,顺序可以是从叶子到根节点,也可以从根到叶子节点。一般树型DP的特征很明显,即状态可以表示为树中的节点,每个节点的状态可以由其子节点状态转移而来(从叶子到根的顺序),或是由其父亲节点转移而来(从根到叶节点的顺序),也可是两者结合。找出状态和状态转移方程仍然是树型DP的关键。
例题
没有上司的晚会
题目描述
Ural大学有N个职员,编号为1~N。他们有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的直接上司。每个职员有一个快乐指数。现在有个周年庆宴会,要求与会职员的快乐指数最大。但是,没有职员愿和直接上司一起参加宴会。
输入
第一行一个整数N。(1≤N≤6000)
接下来N行,第i+1行表示i号职员的快乐指数Ri。(-128≤Ri≤127)
接下来N-1行,每行输入一对整数L,K。表示K是L的直接上司。
最后一行输入0,0。
输出
第1行:输出最大的快乐指数。
样例输入
7
1
1
1
1
1
1
1
1 3
2 3
6 4
7 4
4 5
3 5
0 0
样例输出
5
分析
题意是从N个人中选出若干人,使得其快乐指数最大。但是选人有一些限制条件,即不允许上下级同时出现。
N个人的从属构成一棵树。设f[i][0]表示i不参加晚会,以i为根的子树能达到的最大快乐指数;f[i][1]表示i参加晚会其所在的子树能达到的最大的快乐指数。
则目标状态为max(f[root][0],f[root][1])
状态转移方程f[i][0]=sum(max(f[j][1],f[j][0])) j为i的儿子
f[i][1]=sum([f[j][0])+happy[i] j为i的儿子。
初识状态即叶子节点的状态。f[leaf][0]=0,f[leaf][1]=happy[leaf]。
源代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int n,x,y,root;
bool v[6001],b[6001];
int father[6001],f[6001][2];
void get(int &p){
int flag=1;char ch;
for(p=0;ch>'9'||ch<'0';ch=getchar())
if(ch=='-')
flag=-1;
for(;ch<='9'&&ch>='0';ch=getchar())
p=p*10+ch-'0';
p*=flag;
}
void dp(int i){
v[i]=1;
for(int j=1;j<=n;j++)
if(!v[j]&&father[j]==i){
dp(j);
f[i][1]+=f[j][0];
f[i][0]+=max(f[j][1],f[j][0]);
}
}
int main()
{
get(n);
for(int i=1;i<=n;i++)
get(f[i][1]);
for(int i=1;i<n;i++){
scanf("%d%d",&x,&y);
father[x]=y;b[x]=1;
}
for(int i=1;i<n;i++)
if(!b[i]){
root=i;
break;
}
dp(root);
printf("%d",max(f[root][0],f[root][1]));
}
二叉苹果树
题目描述
有一棵苹果树,如果树枝有分叉,一定是分 2 叉(就是说没有只有 1 个儿子的结点)。这棵树共有 N 个结点(叶子点或者树枝分叉点),编号为1-N,树根编号一定是 1。 我们用一根树枝两端连接的结点的编号来描述一根树枝的位置。下面是一颗有 4 个树枝的树:
现在这颗树枝条太多了,需要剪枝。但是一些树枝上长有苹果。 给定需要保留的树枝数量,求出最多能留住多少苹果。
输入
第1行: 2个空格分开的整数,N 和 Q(1≤Q≤N,1<N≤100),N表示树的结点数,Q表示要保留的树枝数量。 接下来 N-1 行描述树枝的信息。 每行3 个整数,前两个是它连接的结点的编号。第3 个数是这根树枝上苹果的数量。 每根树枝上的苹果不超过30000 个。
输出
第1行:一个整数,表示最多能留住的苹果的数量。
样例输入
5 2
1 3 1
1 4 10
2 3 20
3 5 20
样例输出
21
分析
将枝条上的苹果移到靠近叶子节点的一端的节点上,更好理解。
设f[i][j]表示以i为根的子树保留j个节点所能保留的最大苹果数。
则f[i][j]=max(f[i.leftson][k]+f[i.rightson][j-1-k])+a[i] (0=
源代码
#include<cstdio>
#include<iostream>
using namespace std;
struct node{int l,r,v;}tree[1001];
int flag[101],f[101][101];
int n,q,root;
void get(int &p){
int flag=1;char ch;
for(p=0;ch>'9'||ch<'0';ch=getchar())if(ch=='-') flag=-1;
for(;ch<='9'&&ch>='0';ch=getchar())p=p*10+ch-'0';
p*=flag;
}
void dfs(int i,int j){
int k;
if(!j)f[i][j]=0;
else if(!tree[i].r&&!tree[i].l) f[i][j]=tree[i].v;
else{
f[i][j]=0;
for(k=0;k<j;k++){
if(!f[tree[i].l][k])dfs(tree[i].l,k);
if(!f[tree[i].r][j-k-1])dfs(tree[i].r,j-k-1);
f[i][j]=max(f[i][j],f[tree[i].l][k]+f[tree[i].r][j-k-1]+tree[i].v);
}
}
}
int main()
{
get(n);get(q);
for(int i=1;i<n;i++){
bool p=0;int x,y,m;
get(x);get(y);get(m);
for(int j=1;j<=n;j++)
if(tree[j].l==y||tree[j].r==y)p=1;
if(!p){
if(!tree[x].l)tree[x].l=y;
else if(!tree[x].r)tree[x].r=y;
tree[y].v=m;flag[y]=1;
}
else{
if(!tree[y].l)tree[y].l=x;
else if(!tree[y].r)tree[y].r=x;
tree[x].v=m;flag[x]=1;
}
}
for(int i=1;i<=n;i++)
if(!flag[i]){root=i;break;}
dfs(root,q+1);
printf("%d",f[root][q+1]);
}