本文转自:http://blog.csdn.net/angon823/article/details/52334548
转载博主的树形dp分了入门 一般 难 三部分题目 不定时更新
1、什么是树型动态规划
顾名思义,树型动态规划就是在“树”的数据结构上的动态规划,平时作的动态规划都是线性的或者是建立在图上的,线性的动态规划有二种方向既向前和向后,相应的线性的动态规划有二种方法既顺推与逆推,而树型动态规划是建立在树上的,所以也相应的有二个方向:
1、叶->根:在回溯的时候从叶子节点往上更新信息
2、根 - >叶:往往是在从叶往根dfs一遍之后(相当于预处理),再重新往下获取最后的答案。
不管是 从叶->根 还是 从 根 - >叶,两者都是根据需要采用,没有好坏高低之分。
2、树形动态规划的优美之处
树真的是一种特别特别优美的结构!用来做动态规划也简直是锦上添花再美不过的事,因为树本身至少就有“子结构”性质(树和子树);也本身就具有递归性。所以在树上DP其实是其所当然的事,相比线性动态规划来讲,转移方程更直观,更易理解。
3、难点
1、和线性动态规划相比,树形DP往往是要利用递归的,所以对递归不熟悉的同学,是一道小小的障碍,说是小小的,因为要去理解也不难。
2、细节多,较为复杂的树形DP,从子树,从父亲,从兄弟……已经一些小的要处理的地方,脑子不清晰的时候做起来颇为恶心
3、状态表示和转移方程,也是真正难的地方。做到后面,树形DP的老套路都也就那么多,难的还是怎么能想出转移方程,状压DP、概率DP这些特别的DP应该说做到最后都是这样!
4、补充
建有向图还是无向图? 一般来说我们做树DP都是从上往下递归,如果不乱搞是不会从子节点往根节点的遍历的,这时候可以建有向图,只加边一次就可以,对于有“思想洁癖”的人来说,如果加一次边就可以再让他加两次是很难受的。但是有些题目可能很隐蔽的某个地方涉及到从子节点往上访问,就会错的很惨。所以,一般不非常确定建有向图就可的话还是建无向图吧。
入门题A:
Anniversary party (hdu1520) 没有上司的晚会
题目描述
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
代码没有提交 数组大小随便开的 输入输出都对 只是大体写个思路
dp[i][0]:不邀请的值
dp[i][1]:邀请的值
如果不邀请i,那么i的下级可能去也可能不去,即:dp[node][0]+=max(dp[i][0],dp[i][1]);
如果邀请i,那么i的下级肯定不去,只加上不去值就行 dp[node][1]+=dp[i][0];
#include<stdio.h>
int dp[100][100];
int node,n;
int vis[100]= {0};
int father[100];
int son[100]= {0};
int max(int a,int b)
{
return a>b?a:b;
}
void dfs(int node)
{
vis[node]=1;
for(int i=1; i<=n; i++)
{
if(vis[i]==0&&father[i]==node)
{
dfs(i);
dp[node][1]+=dp[i][0];
dp[node][0]+=max(dp[i][0],dp[i][1]);
}
}
}
int main()
{
scanf("%d",&n);
for(int i=1; i<=n; i++)
scanf("%d",&dp[i][1]);
int l,k;
while(~scanf("%d%d",&l,&k))
{
if(l==0&&k==0)
break;
father[l]=k;
son[l]++;//记录l的所有上级数目
}
for(int i=1; i<=n; i++)
{
if(son[i]==0)//所有上级数目是0,即根节点
{
node=i;
break;
}
}
dfs(node);
printf("%d\n",max(dp[node][0],dp[node][1]));
return 0;
}