树形DP(Simple Tree,玲珑杯 Round#8 B lonlife 1080)

15 篇文章 0 订阅
6 篇文章 0 订阅

一开始总觉得是dfs序之类的问题,子树中的数量很好解决,一直在思考如何往上求数量。很久后才注意到边带权,然后只好放弃dfs序,只能考虑树形DP了。然后就是经典的求树的直径的改造。然后写搓了(大概就是第一遍dfs的时候维护的量不够多,所以在第二次dfs时就得用更多的运算量去弥补,即没有用更多的空间去换时间,所以超时了,以前这么写都能过啊,这次就不行了,更重要的是自己写得很不熟练,很多细节都搞错了,WA了好几发)。


以后自己一定要多看几遍题目,想一会,看一会,这样可以保证自己对题目有深刻的印象,防止自己理解有偏差或者想着想着就忘记了题目的限制条件。最后经典题目一定要做熟练,否则到了战场上还要再复习,还要一点一点磨,像什么样。


解题思路,就是经典树形DP——求树的直径的略加改编。

第一遍dfs求所有节点所在子树的(最大深度,对应的点的个数)和(次大深度,对应的点的个数)。

然后第二遍dfs求所有节点往上走的(最大长度,对应的点的个数)。

输出时循环+讨论一下即可。


搓代码

#include<bits/stdc++.h>
#define maxn 100010
using namespace std;

int N;
vector<int>G[maxn];
vector<int>W[maxn];
int d1[maxn],cd1[maxn],d2[maxn],cd2[maxn],df[maxn];
int up[maxn],cup[maxn];
int d[maxn],cd[maxn];

void dfs1(int u,int f)
{
    d1[u]=d2[u]=cd1[u]=cd2[u]=-1;
    for(unsigned int i=0;i<G[u].size();i++)
    {
        int v=G[u][i];
        if(v==f)
        {
            df[u]=W[u][i];
            continue;
        }
        dfs1(v,u);
        int D=W[u][i]+d1[v];
        if(D>d1[u])
        {
            d2[u]=d1[u];
            cd2[u]=cd1[u];
            d1[u]=D;
            cd1[u]=cd1[v];
        }
        else if(D==d1[u]) cd1[u]+=cd1[v];
        else if(D>d2[u])
        {
            d2[u]=D;
            cd2[u]=cd1[v];
        }
        else if(D==d2[u]) cd2[u]+=cd1[v];
    }
    if(d1[u]==-1)
    {
        d1[u]=0;
        cd1[u]=1;
    }
}

void dfs2(int u,int f)
{
    up[u]=up[f]+df[u];
    cup[u]=cup[f];
    if(!(d1[u]+df[u]==d1[f]&&cd1[f]==cd1[u]))
    {
        int D=d1[f]+df[u];
        if(D>up[u])
        {
            up[u]=D;
            cup[u]=cd1[f];
            if(d1[u]+df[u]==d1[f]) cup[u]-=cd1[u];
        }
        else if(D==up[u])
        {
            cup[u]+=cd1[f];
            if(d1[u]+df[u]==d1[f]) cup[u]-=cd1[u];
        }
    }
    else if(cd2[f]!=-1)
    {
        if(d1[f]==d1[u]+df[u])
        {
            int D=d2[f]+df[u];
            if(D>up[u])
            {
                up[u]=D;
                cup[u]=cd2[f];
            }
            else if(D==up[u]) cup[u]+=cd2[f];
        }
        else
        {
            int D=d1[f]+df[u];
            if(D>up[u])
            {
                up[u]=D;
                cup[u]=cd1[f];
                if(d1[u]+df[u]==d1[f]) cup[u]-=cd1[u];
            }
            else if(D==up[u])
            {
                cup[u]+=cd1[f];
                if(d1[u]+df[u]==d1[f]) cup[u]-=cd1[u];
            }
        }
    }
    if(d1[u]==up[u])
    {
        d[u]=d1[u];
        cd[u]=cd1[u]+cup[u];
    }
    else if(d1[u]>up[u])
    {
        d[u]=d1[u];
        cd[u]=cd1[u];
    }
    else
    {
        d[u]=up[u];
        cd[u]=cup[u];
    }
    for(unsigned int i=0;i<G[u].size();i++)
    {
        int v=G[u][i];
        if(v==f) continue;
        dfs2(v,u);
    }
}

int kase;

int main()
{
    up[0]=0;
    cup[0]=1;
    G[0].push_back(1);
    W[0].push_back(0);
    while(~scanf("%d",&N))
    {
        for(int i=1;i<=N;i++)
        {
            G[i].clear();
            W[i].clear();
        }
        int u,v,w;
        for(int i=1;i<N;i++)
        {
            scanf("%d %d %d",&u,&v,&w);
            G[u].push_back(v);
            W[u].push_back(w);
            G[v].push_back(u);
            W[v].push_back(w);
        }
        dfs1(1,0);
        dfs2(1,0);
        printf("Case #%d:",++kase);
        for(int i=1;i<=N;i++)
            printf(" %d",cd[i]);
        puts("");
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值