ACM-11月19日周日周末训练心得

    这周看了树形DP的一些博客,比如一道compuer的题目要去求树上每个点的最远距离是多少,这题一直被称为树形dp的经典是有它的道理的,因为树dp就是把dp放到树上做了,一般是从上到下或从下到上(利用回溯)的移转状态而这题很合适的需要两次dfs。对于<u,v>(有向),dp[u][0]表示在u的子树下u的最远距离是多少dp[u][1]表示在u的子树(和dp[u][0]不是同一孩子)u的次远距离是多少dp[u][2]表示通过u的父亲能走的最远距离是多少第一次从下到上,对于<u,v>(有向),状态转移显然是 dp[u][0] = dp[v][0]+w[i];所以要先算出dp[v][0]才能知道dp[u][0]。故是从下往上。第二次从上往下,其实就是再遍历一边图,把dp[v][2]算出来,显然:dp[v][2] = max(dp[u][2],dp[v][0]+w[i]==dp[u][0]?dp[u][1]:dp[u][0]) + w[i];要算dp[v][2],要先算dp[u][0],所以从上往下。最后的答案就是 max(dp[u][0],dp[u][2])。

 

void dfs1(int u,int v) //每个节点子树下的最大和次大  
{  
    for(int i=head[u];i!=-1;i=edge[i].next)  
    {  
        int v=edge[i].v;  
        dfs1(v,u);  
        int w=edge[i].w;  
        int temp = dp[v][0] + w;  
        if(temp >= dp[u][0])  
        {  
            dp[u][1]=dp[u][0];  
            dp[u][0] = temp;  
        }  
        else if(temp > dp[u][1])  
            dp[u][1] = temp;  
    }   
}   
void dfs2(int u,int v)  
{  
    for(int i=head[u];i!=-1;i=edge[i].next)  
    {  
        int v=edge[i].v;  
        if(dp[u][0] == dp[v][0] + edge[i].w)  
        {  
            dp[v][2] = max(dp[u][2],dp[u][1]) + edge[i].w;  
        }  
        else  
        {  
            dp[v][2] = max(dp[u][2],dp[u][0]) + edge[i].w;  
        }  
        dfs2(v,u);  
    }  
}

    对于HDU1520,给一棵树,选最多的结点使得选择的结点不存在直接的父子关系,很容易想到一个结点有两个状态:选或者不选
,所以自然地想到状态dp[u][0/1]表示u子树内的最佳答案,u的状态为选或者不选,初始化自然是叶子结点dp[u][0]=0,dp[u][1]=w[u],转移则可以考虑依次考虑,u不选的时候:u的儿子可以任意选或者不选,所以dp[u][0]+=max(dp[v][0],dp[v][1]),u选的时候:u的儿子必定不能选,所以dp[u][1]+=dp[v][0]   然后dp[u][1]+=w[u]表示加上u这个点,答案自然就是max(dp[rt][0],dp[rt][1])了。
 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值