HZAU 1201 Friends(树形dp)

本文介绍了一道关于树形动态规划的经典题目——求每个节点距离六个点以内的点的数量。通过具体实例详细解析了树形DP的两种方向:叶->根与根->叶,并给出了完整的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原题

1201: Friends

Time Limit: 1 Sec  Memory Limit: 1280 MB

Description


    In a country, the relationship between people can be indicated by a tree. If two people are acquainted with each other, there will be an edge between them. If a person can get in touch with another through no more than five people, we should consider these two people can become friends. Now, give you a tree of N people’s relationship. ( 1 <= N <= 1e5), you should compute the number of who can become friends of each people?  

Input


    In the first line, there is an integer T, indicates the number of the cases.
    For each case, there is an integer N indicates the number of people in the first line.
   

In the next N-1 lines, there are two integers u and v, indicate the people u and the people v are acquainted with each other directly. People labels from 1.  

Output


    For each case, the first line is “Case #k :”, k indicates the case number.

    In the next N lines, there is an integer in each line, indicates the number of people who can become the ith person’s friends. i is from 1 to n.  

Sample Input


1
8
1 2
2 3
3 4
4 5
5 6
6 7
7 8

Sample Output


Case #1:
6
7
7
7
7
7
7
6

题意

给出一棵树,问每个节点距离六个点以内的点有几个。

思路

树形dp,设Dw[x][i]数组表示从x结点往下距离为i的结点的个数,则可表示为所有儿子结点往下距离为i-1的结点个数之和,有Dw[x][i]=sum(Dw[son][i-1])。设Up[x][i]数组表示从x结点往上距离为i的结点的个数,可表示为父亲结点往上距离为i-1的结点数与兄弟结点往下距离为i-1的结点的个数。其中兄弟的部分可表示为父亲结点往下距离为i-1的结点数减去该结点往下距离为i-2的节点数。有Up[x][i]=Up[fx][i-1]+Dw[fx][i-1]-Dw[x][i>=2?i-2:0]。用两个dfs分别计算出各个结点的Dw和Up值再相加,就可以得到每个点的答案了。

涉及知识及算法

树形dp:顾名思义,树型动态规划就是在“树”的数据结构上的动态规划,平时作的动态规划都是线性的或者是建立在图上的,线性的动态规划有二种方向既向前和向后,相应的线性的动态规划有二种方法既顺推与逆推,而树型动态规划是建立在树上的,所以也相应的有二个方向:
    1、叶->根:在回溯的时候从叶子节点往上更新信息
    2、根 - >叶:往往是在从叶往根dfs一遍之后(相当于预处理),再重新往下获取最后的答案。

    不管是 从叶->根 还是 从 根 - >叶,两者都是根据需要采用,没有好坏高低之分。

代码

    #include <cstdio>
    #include <algorithm>
    #include <vector>
    #include <cstring>
    using namespace std;
    const int N=100010;
    vector<int> v[N];
    int Dw[N][10],Up[N][10];    //第一个下标内的是当前结点,第二个下标内是距离
    void dfs(int x,int fx){
        Dw[x][0]=1;
        for(int i=0;i<v[x].size();i++)if(v[x][i]!=fx){       //对儿子结点搜素
            dfs(v[x][i],x);
            for(int j=1;j<=6;j++)Dw[x][j]+=Dw[v[x][i]][j-1];  //Dw[x][i]=sum(Dw[son][i-1])
        }
    }
    void dfs2(int x,int fx){
        Up[x][0]=1;
        if(x!=fx){
            for(int i=1;i<=6;i++)
                Up[x][i]=Up[fx][i-1]+Dw[fx][i-1]-Dw[x][i>=2?i-2:0];
        }
        for(int i=0;i<v[x].size();i++) if(v[x][i]!=fx) dfs2(v[x][i],x);   //对儿子结点搜索
    }
    int T,n;
    int main(){
        scanf("%d",&T);
        for(int cas=1;cas<=T;cas++){
            printf("Case #%d:\n",cas);
            scanf("%d",&n);
            for(int i=1;i<=n;i++)v[i].clear();  //清空容器
            memset(Dw,0,sizeof(Dw));
            memset(Up,0,sizeof(Up));
            for(int i=1;i<n;i++){
                int x,y;
                scanf("%d%d",&x,&y);
                v[x].push_back(y);  //在vector尾部加入y
                v[y].push_back(x);
            }dfs(1,1); dfs2(1,1);
            for(int i=1;i<=n;i++){
                int res=0;
                for(int j=1;j<=6;j++)res=res+Up[i][j]+Dw[i][j];
                printf("%d\n",res);
            }
        }return 0;
    }
 
/**************************************************************
    Problem: 1201
    Language: C++
    Result: Accepted
    Time:383 ms
    Memory:16020 kb
****************************************************************/
文章大部分内容转载自博客园博主forever97 http://www.cnblogs.com/forever97/p/hzau1201.html;

“涉及知识及算法”板块引用自csdn博主is_angon http://blog.youkuaiyun.com/angon823/article/details/52334548,

向他们表示感谢。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值