树形dp

本文转自: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
输入










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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值