hdu3534,个人认为很经典的树形dp

题目大意为,求一个树的直径(最长路),以及直径的数量

朴素的dp只能找出某点开始的最长路径,但这个最长路径却不一定是树的直径,本弱先开始就想简单了,一直wa

直到我看了某位大牛的题解。。。

按照那位大牛的思路,我们来考虑直径的构成:

情况1:由某叶子节点出发产生的最长路径直接构成

情况2:由某有多个儿子的节点出发产生的两条长路径组成,这其中,又可以分为两条长路径长度相等与否两种情况

所以 在dp的时候,我们需要记录每个节点出发产生的最长路径和次长路径,以及他们的数量,数量的统计也是非常麻烦

详细请见代码:

#include<stdio.h>
#include<iostream>
#include<stdlib.h>
#include<math.h>
#include<ctype.h>
#include<algorithm>
#include<string>
#include<string.h>
#include<queue>
#define mod 998244353
#define MAX 100000000
using namespace std;
int t,n,m,p,k,tt,f;
int x;

int head[10010];
typedef struct Node
{
    int en;
    int value;
    int next;
}node;
node edge[20010];
typedef struct DPnode
{
    int dp1,dp2,len,nn;
    int n1,n2;
}DP;
DP dp[10010];
void ini()
{
    int x,y,z;
    for(int i=1;i<=n-1;i++)
    {
        scanf("%d%d%d",&x,&y,&k);
        edge[2*i-1].en=y;
        edge[2*i-1].next=head[x];
        edge[2*i-1].value=k;
        head[x]=2*i-1;
        edge[2*i].en=x;
        edge[2*i].next=head[y];
        edge[2*i].value=k;
        head[y]=2*i;
    }
}
void dfs(int s,int p)
{
    dp[s].dp1=dp[s].dp2=dp[s].len=dp[s].n1=dp[s].n2=dp[s].nn=0;
    int leaf=1;
    for(int i=head[s];i;i=edge[i].next)
    {
        int q=edge[i].en;
        if(q==p)
            continue;
        leaf=0;
        dfs(q,s);
        int tmp=dp[q].dp1+edge[i].value;
        if(tmp>dp[s].dp1)
        {
            dp[s].dp2=dp[s].dp1;
            dp[s].n2=dp[s].n1;
            dp[s].dp1=tmp;
            dp[s].n1=dp[q].n1;
        }
        else if(tmp==dp[s].dp1)
        {
            dp[s].n1+=dp[q].n1;
        }
        else if(tmp>dp[s].dp2)
        {
            dp[s].dp2=tmp;
            dp[s].n2=dp[q].n1;
        }
        else if(tmp==dp[s].dp2)
        {
            dp[s].n2+=dp[q].n1;
        }
    }
    if(leaf)
    {
        dp[s].n1=1;dp[s].nn=1;
        dp[s].len=0;
        dp[s].dp1=0;
        return;
    }
    int c1=0,c2=0;
    for(int i=head[s];i;i=edge[i].next)
    {
        int q=edge[i].en;
        if(q==p)
            continue;
        int tmp=dp[q].dp1+edge[i].value;
        if(tmp==dp[s].dp1)
            c1++;
        else if(tmp==dp[s].dp2&&dp[s].dp2)
            c2++;
    }
    if(c1>1)
    {
        dp[s].len=dp[s].dp1*2;
        int sum=0;
        for(int i=head[s];i;i=edge[i].next)
        {
            int q=edge[i].en;
            if(q==p)
                continue;
            if(dp[q].dp1+edge[i].value==dp[s].dp1)
            {
                dp[s].nn+=sum*dp[q].n1;
                sum+=dp[q].n1;
            }
        }
    }
    else if(c2>0)
    {
        dp[s].len=dp[s].dp1+dp[s].dp2;
        for(int i=head[s];i;i=edge[i].next)
        {
            int q=edge[i].en;
            if(q==p)
                continue;
            if(dp[q].dp1+edge[i].value==dp[s].dp2)
            {
                dp[s].nn+=dp[s].n1*dp[q].n1;
            }
        }
    }
    else
    {
        dp[s].len=dp[s].dp1;
        dp[s].nn=dp[s].n1;
    }
    return ;
}
void solve()
{
    int ans=0;
    int num=0;
    for(int i=1;i<=n;i++)
    {
        if(dp[i].len>ans)
        {
            ans=dp[i].len;
            num=dp[i].nn;
        }
        else if(dp[i].len==ans)
        {
            num+=dp[i].nn;
        }
    }
    printf("%d %d\n",ans,num);
}
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        memset(head,0,sizeof(head));
        ini();
        dfs(1,0);
        solve();
    }
    return 0;
}
时间: 2024-10-13 11:25:08

hdu3534,个人认为很经典的树形dp的相关文章

CF767C 经典的树形DP

n个节点的树第i个节点权值为a[i]n<=10^6-100<=a[i]<=100问是否能够删除掉两条边,使得该树分成三个不为空,并且每部分权值之和相等.无解输出-1 否则输出要删除边(u->v)的v节点序号. 以上是题面.如果觉得这个题面看不懂的话可以去CodeForces现场看一下(逃)下面是解法.考虑存在这么三个子树,那么这个树肯定就能分成这么三个子树,而这个树的点权之和肯定能平均分成三个子树,那么这个树的点权 mod 3 肯定为0.这是一个显然的剪枝.言归正传,我们来说说正解

ZOJ2599:Graduated Lexicographical Ordering(很经典的数位DP)

Consider integer numbers from 1 to n. Let us call the sum of digits of an integer number its weight. Denote the weight of the number x as w(x). Now let us order the numbers using so called graduated lexicographical ordering, or shorter grlex ordering

SGU 149. Computer Network( 树形dp )

题目大意:给N个点,求每个点的与其他点距离最大值 很经典的树形dp...很久前就想写来着...看了陈老师的code才会的...mx[x][0], mx[x][1]分别表示x点子树里最长的2个距离, dfs一遍得到. mx[x][2]表示从x的父亲到x的最长路径长度, 也是dfs一遍得到(具体看代码).最后答案就是max(mx[x][0], mx[x][2]). 时间复杂度O(N) ----------------------------------------------------------

蓝桥杯 算法拔高 金属采集 [ 树形dp 经典 ]

传送门 算法提高 金属采集 时间限制:1.0s   内存限制:256.0MB 锦囊1 锦囊2 锦囊3 问题描述 人类在火星上发现了一种新的金属!这些金属分布在一些奇怪的地方,不妨叫它节点好了.一些节点之间有道路相连,所有的节点和道路形成了一棵树.一共有 n 个节点,这些节点被编号为 1~n .人类将 k 个机器人送上了火星,目的是采集这些金属.这些机器人都被送到了一个指定的着落点, S 号节点.每个机器人在着落之后,必须沿着道路行走.当机器人到达一个节点时,它会采集这个节点蕴藏的所有金属矿.当机

树形DP 学习总结

DP毕竟是算法中最精妙的部分,理解并玩得花哨还是需要一定的时间积累 之前对普通的DP也不敢说掌握,只能说略懂皮毛 在学习树形DP 的同时也算是对DP有了更深的理解吧 DP的关键就在于状态的定义以及找转移 首先要考虑清楚状态,状态要能够很好地并且完整地描述子问题 其次考虑最底层的状态,这些状态一般是最简单的情况或者是边界情况 再就是考虑某一个状态能从哪些子状态转移过来,同时还要考虑转移的顺序,确保子问题已经解决 树形DP很多时候就是通过子节点推父亲节点的状态 还是通过题目来加强理解吧 1.HDU

Luogu P2014 选课 (树形DP)

题目 题目描述 在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习,在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习.现在有N门功课,每门课有个学分,每门课有一门或没有直接先修课(若课程a是课程b的先修课即只有学完了课程a,才能学习课程b).一个学生要从这些课程里选择M门课程学习,问他能获得的最大学分是多少? 输入输出格式 输入格式: 第一行有两个整数N,M用空格隔开.(1<=N<=300,1<=M<=300) 接下来的N行,第i+1行

树形dp 入门

今天学了树形dp,发现树形dp就是入门难一些,于是好心的我便立志要发一篇树形dp入门的博客了. 树形dp的概念什么的,相信大家都已经明白,这里就不再多说.直接上例题. 一.常规树形DP P1352 没有上司的舞会 题目描述 某大学有N个职员,编号为1~N.他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的直接上司.现在有个周年庆宴会,宴会每邀请来一个职员都会增加一定的快乐指数Ri,但是呢,如果某个职员的上司来参加舞会了,那么这个职员就无论如何也不肯来参加舞会了.所以

树形DP——动态规划与数据结构的结合,在树上做DP

本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是算法与数据结构的第15篇,也是动态规划系列的第4篇. 之前的几篇文章当中一直在聊背包问题,不知道大家有没有觉得有些腻味了.虽然经典的文章当中背包一共有九讲,但除了竞赛选手,我们能理解到单调优化就已经非常出色了.像是带有依赖的背包问题,和混合背包问题,有些剑走偏锋,所以这里不多做分享.如果大家感兴趣可以自行百度背包九讲查看,今天我们来看一个有趣的问题,通过这个有趣的问题,我们来了解一下在树形结构当中做动态规划的方法. 这个问题题意很

HDU2196 Computer(树形DP)

和LightOJ1257一样,之前我用了树分治写了.其实原来这题是道经典的树形DP,感觉这个DP不简单.. dp[0][u]表示以u为根的子树中的结点与u的最远距离 dp[1][u]表示以u为根的子树中的结点与u的次远距离 这两个可以一遍dfs通过儿子结点转移得到.显然dp[0][u]就是u的一个可能的答案,即u往下走的最远距离,还缺一部分就是u往上走的最远距离: dp[2][u]表示u往上走的最远距离 对于这个的转移,分两种情况,是这样的: dp[2][v] = max( dp[0][u]+w