• 题解报告:hihoCoder #1050 : 树中的最长路


    描述

    上回说到,小Ho得到了一棵二叉树玩具,这个玩具是由小球和木棍连接起来的,而在拆拼它的过程中,小Ho发现他不仅仅可以拼凑成一棵二叉树!还可以拼凑成一棵多叉树——好吧,其实就是更为平常的树而已。

    但是不管怎么说,小Ho喜爱的玩具又升级换代了,于是他更加爱不释手(其实说起来小球和木棍有什么好玩的是吧= =)。小Ho手中的这棵玩具树现在由N个小球和N-1根木棍拼凑而成,这N个小球都被小Ho标上了不同的数字,并且这些数字都是出于1..N的范围之内,每根木棍都连接着两个不同的小球,并且保证任意两个小球间都不存在两条不同的路径可以互相到达。总而言之,是一个相当好玩的玩具啦!

    但是小Hi瞧见小Ho这个样子,觉得他这样沉迷其中并不是一件好事,于是寻思着再找点问题让他来思考思考——不过以小Hi的水准,自然是手到擒来啦!

    于是这天食过早饭后,小Hi便对着又拿着树玩具玩的不亦乐乎的小Ho道:“你说你天天玩这个东西,我就问你一个问题,看看你可否知道?”

    “不好!”小Ho想都不想的拒绝了。

    “那你就继续玩吧,一会回国的时候我不叫上你了~”小Hi严肃道。

    “诶!别别别,你说你说,我听着呢。”一向习惯于开启跟随模式的小Ho忍不住了,马上喊道。

    小Hi满意的点了点头,随即说道:“这才对嘛,我的问题很简单,就是——你这棵树中哪两个结点之间的距离最长?当然,这里的距离是指从一个结点走到另一个结点经过的木棍数。”。

    “啊?”小Ho低头看了看手里的玩具树,困惑了。

    提示一:路总有折点,路径也不例外!

    输入

    每个测试点(输入文件)有且仅有一组测试数据。

    每组测试数据的第一行为一个整数N,意义如前文所述。

    每组测试数据的第2~N行,每行分别描述一根木棍,其中第i+1行为两个整数Ai,Bi,表示第i根木棍连接的两个小球的编号。

    对于20%的数据,满足N<=10。

    对于50%的数据,满足N<=10^3。

    对于100%的数据,满足N<=10^5,1<=Ai<=N, 1<=Bi<=N

    小Hi的Tip:那些用数组存储树边的记得要开两倍大小哦!

    输出

    对于每组测试数据,输出一个整数Ans,表示给出的这棵树中距离最远的两个结点之间相隔的距离。

    样例输入

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

    样例输出

    6
    解题思路:题意:有n个点,它们之间有n-1条无向边,形成一棵树,并且保证任意两个点间都不存在两条不同的路径可以互相到达。求这棵树中哪两个结点之间的距离最长?这里的距离是指从一个结点走到另一个结点经过的边数。
    求树的直径(最长路),也就是图中某两点的最长距离。做法:随便以某一个点开始dfs(bfs)找到深度最大的便是直径的某一端点t,然后从这个点t再dfs(bfs)一次就可以找出直径的另一端点s,这样s---t就是树的直径,也就是常说的树上最长路。为什么可以随便以一个点开始就能找到直径的某一端点呢?请看这篇博文(不难理解):树的直径最长路证明

    AC代码一(129ms):两次dfs。时间复杂度为0(2E)。
     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 const int maxn=1e5+5;
     4 vector<int> vec[maxn];
     5 int n,u,v,maxdep,maxvex;bool vis[maxn];
     6 void dfs(int x,int dep){
     7     vis[x]=true;
     8     int sz=vec[x].size();
     9     if(sz==1&&dep>maxdep){maxdep=dep;maxvex=x;}//找到离当前根节点最远的叶子节点,更新深度值和叶子节点编号
    10     for(int i=0;i<sz;++i)//遍历其邻接点
    11         if(!vis[vec[x][i]])dfs(vec[x][i],dep+1);
    12 }
    13 int main(){
    14     while(~scanf("%d",&n)){
    15         for(int i=1;i<=n;++i)vec[i].clear();
    16         while(--n){
    17             scanf("%d%d",&u,&v);
    18             vec[u].push_back(v);
    19             vec[v].push_back(u);
    20         }
    21         maxdep=0,maxvex=1;
    22         memset(vis,false,sizeof(vis));
    23         dfs(1,0);//第一次随便以某个点为根节点,找树的直径的某一端点maxvex
    24         memset(vis,false,sizeof(vis));maxdep=0;
    25         dfs(maxvex,0);//第二次从maxvex去找树直径的另一端点
    26         printf("%d
    ",maxdep);
    27     }
    28     return 0;
    29 }

    AC代码二(89ms):两次bfs。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 const int maxn=1e6+5;
     4 struct EDGE{int to,next;}edge[maxn<<1];
     5 struct node{
     6     int u,dep;
     7     node(int x,int y):u(x),dep(y){}
     8 };
     9 int n,x,y,cnt,res,maxdep,maxvex,head[maxn];bool vis[maxn];
    10 queue<node> que;
    11 void add_edge(int u,int v){
    12     edge[cnt].to=v;
    13     edge[cnt].next=head[u];
    14     head[u]=cnt++;
    15 }
    16 void bfs(int u,int dep,int &maxdep,int &maxvex){
    17     while(!que.empty())que.pop();
    18     memset(vis,false,sizeof(vis));
    19     que.push(node(u,dep));vis[u]=true;
    20     while(!que.empty()){
    21         node nod=que.front();que.pop();
    22         for(int i=head[nod.u];~i;i=edge[i].next){
    23             int v=edge[i].to;
    24             if(!vis[v]){
    25                 vis[v]=true;
    26                 que.push(node(v,nod.dep+1));
    27             }
    28         }
    29         if(maxdep<nod.dep)maxdep=nod.dep,maxvex=nod.u;//取最深
    30     }
    31 }
    32 int main(){
    33     while(~scanf("%d",&n)){
    34         memset(head,-1,sizeof(head));cnt=0;
    35         while(--n){
    36             scanf("%d%d",&x,&y);
    37             add_edge(x,y);
    38             add_edge(y,x);
    39         }
    40         maxdep=0,maxvex=1;
    41         bfs(1,0,maxdep,maxvex);maxdep=0;
    42         bfs(maxvex,0,maxdep,maxvex);
    43         printf("%d
    ",maxdep);
    44     }
    45     return 0;
    46 }

    AC代码三(79ms):一次dfs。这里用到了一个树的性质:树的直径的长度一定会是某个点t的最长距离first(t)与次长距离second(t)之和。最后求出max{first(t),second(t)}就可以了。如果用first(t),second(t)分别表示以t为根节点的子树中最长路和次长路的长度,那么只需要求出t的所有子结点的first值,first(t)便是这些first值中的最大值+1,second(t)便是这些first值中的次大值+1。时间复杂度为O(E)。

      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 const int maxn=1e6+5;
      4 struct node{int to,next;}edge[maxn<<1];//无向图双向边,2倍边数
      5 int n,x,y,cnt,res,head[maxn];
      6 void add_edge(int u,int v){//链式前向星
      7     edge[cnt].to=v;
      8     edge[cnt].next=head[u];//第cnt条边记录上一次起点为u的边的编号
      9     head[u]=cnt++;//head[u]表示当前以u为起点的第cnt条边
     10 }
     11 int dfs(int u,int fa,int &maxdist){
     12     int Dmax=0,Dsec=0;//每一个子树的根节点都有一个最长距离和次长距离,因此要重新定义,不然会出错
     13     for(int i=head[u];~i;i=edge[i].next){
     14         //printf("第%d条边
    ",i/2);
     15         int v=edge[i].to;//取出子树节点
     16         //cout<<"fa:"<<fa<<",u:"<<u<<",v:"<<v<<endl;
     17         if(v^fa){//避免再次遍历到父节点
     18             //cout<<"u:"<<u<<' '<<"v:"<<v<<endl;
     19             int nowd=dfs(v,u,maxdist)+1;
     20             if(nowd>Dmax)Dsec=Dmax,Dmax=nowd;
     21             else if(nowd>Dsec)Dsec=nowd;
     22             //求出t的所有子结点的Dmax(first)值,first(t)便是这些first值中的最大值+1,second(t)便是这些first值中的次大值+1.
     23             //cout<<u<<"--->"<<v<<"、子树的最长深度:"<<nowd<<",第一长:"<<Dmax<<",第二长:"<<Dsec<<endl;
     24         }
     25     }
     26     maxdist=max(maxdist,Dmax+Dsec);//更新树的直径:最长+次长
     27     //cout<<"目前最长的距离"<<maxdist<<endl;
     28     return Dmax;//返回当前以u为根的子树中的最大深度
     29 }
     30 int main(){
     31     while(~scanf("%d",&n)){
     32         memset(head,-1,sizeof(head));cnt=0;
     33         while(--n){
     34             scanf("%d%d",&x,&y);
     35             add_edge(x,y);
     36             add_edge(y,x);
     37         }
     38         int maxlen=0;
     39         dfs(1,-1,maxlen);
     40         printf("%d
    ",maxlen);
     41     }
     42     return 0;
     43 }
     44 /**
     45 样例模拟过程如下:
     46 8
     47 1 2
     48 1 3
     49 1 4
     50 4 5
     51 3 6
     52 6 7
     53 7 8
     54 第2条边
     55 fa:-1,u:1,v:4
     56 u:1 v:4
     57 第3条边
     58 fa:1,u:4,v:5
     59 u:4 v:5
     60 第3条边
     61 fa:4,u:5,v:4
     62 目前最长的距离0
     63 4--->5、子树的最长深度:1,第一长:1,第二长:0
     64 第2条边
     65 fa:1,u:4,v:1
     66 目前最长的距离1
     67 1--->4、子树的最长深度:2,第一长:2,第二长:0
     68 第1条边
     69 fa:-1,u:1,v:3
     70 u:1 v:3
     71 第4条边
     72 fa:1,u:3,v:6
     73 u:3 v:6
     74 第5条边
     75 fa:3,u:6,v:7
     76 u:6 v:7
     77 第6条边
     78 fa:6,u:7,v:8
     79 u:7 v:8
     80 第6条边
     81 fa:7,u:8,v:7
     82 目前最长的距离1
     83 7--->8、子树的最长深度:1,第一长:1,第二长:0
     84 第5条边
     85 fa:6,u:7,v:6
     86 目前最长的距离1
     87 6--->7、子树的最长深度:2,第一长:2,第二长:0
     88 第4条边
     89 fa:3,u:6,v:3
     90 目前最长的距离2
     91 3--->6、子树的最长深度:3,第一长:3,第二长:0
     92 第1条边
     93 fa:1,u:3,v:1
     94 目前最长的距离3
     95 1--->3、子树的最长深度:4,第一长:4,第二长:2
     96 第0条边
     97 fa:-1,u:1,v:2
     98 u:1 v:2
     99 第0条边
    100 fa:1,u:2,v:1
    101 目前最长的距离3
    102 1--->2、子树的最长深度:1,第一长:4,第二长:2
    103 目前最长的距离6
    104 6
    105 **/

  • 相关阅读:
    焦点事件中的Validating处理方法
    推荐一个快速反射调用的类
    VB.NET自我总结语法
    WinForm应用程序实现虚拟键盘
    将图片保存到XML文件的方法
    分享TextBoxLineEx控件
    自定义CancelEventArgs类,封装事件参数信息,实现e.Cancle=true取消机制。
    从sql server 中读取二进制图片
    Oracle数据库自我总结
    Android DroidDraw UI设计工具下载地址
  • 原文地址:https://www.cnblogs.com/acgoto/p/9563328.html
Copyright © 2020-2023  润新知