zoukankan      html  css  js  c++  java
  • [Usaco2004Feb]Cow Marathon 树的直径

    本人水平有限,题解不到为处,请多多谅解

    本蒟蒻谢谢大家观看

    题目传送门

    先来说下什么是树的直径

    树的直径:一棵树中两个节点所经过的权值和最大即为树的直径,

    即树中所有最短路径长度中的最大值

    图示:

    红笔所画的即为树的直径,由 2→5 所经过的所有点为树的直径权值和为:20+3+13+9+7==52

    即ans==52

    法一:跑两遍dfs

    先从任意一点P出发,找离它最远的点Q,再从点Q出发,找离它最远的点W,W到Q的距离就是树的直径

    证明博客

    其实可以根据上面一幅图感性理解一下,分两种情况。

    1:若P在直径上,毫无疑问,Q一定在直径的端点上(左右端点),所以保证了W&Q的距离就是树的直径

    2:若P不在直径上,例如令P==7时,其Q也一定在直径端点上,这时Q==5,所以也可证明W&Q的距离就是树的直径

    先以P为根做一遍dfs,再以Q为根再做一遍dfs,f[Q]为自己到自己的值为0,dfs(Q,0)表示其父亲节点为0

    code:

     1 #include<bits/stdc++.h> 
     2 using namespace std;
     3 int n,m;
     4 int tot,ver[100010],edge[100010],head[100010],nxt[100010];
     5 int x,y,z,ans,hhh;
     6 char ch;
     7 int f[101000];
     8 bool v[1000010];
     9 void add(int x,int y,int z)
    10 {
    11     ++tot;
    12     ver[tot]=y;
    13     edge[tot]=z;
    14     nxt[tot]=head[x];
    15     head[x]=tot;
    16 }
    17 inline long long read(){
    18     register long long x(0), f=1; register char c(getchar());
    19     while(c<'0'||'9'<c){ if(c=='-') f=-1; c=getchar(); }
    20     while('0'<=c&&c<='9')
    21         x=(x<<1)+(x<<3)+(c^48), c=getchar();
    22     return x*f;
    23 }
    24 void dfs(int x,int fa)
    25 {
    26     for(int i=head[x];i;i=nxt[i])
    27     {
    28         int y=ver[i],z=edge[i];
    29         if(y==fa)continue;
    30             f[y]=f[x]+z;
    31             if(f[y]>ans)
    32             {
    33                 ans=f[y];
    34                 hhh=y;
    35             }
    36             dfs(y,x);
    37     }
    38 }
    39 int main()
    40 {
    41     n=read();
    42     m=read();
    43     for(int i=1;i<=m;i++)
    44     {
    45         x=read();
    46         y=read();
    47         z=read();
    48         cin>>ch;
    49         add(x,y,z);
    50         add(y,x,z);
    51     }
    52     f[1]=0;
    53     dfs(1,0);
    54     f[hhh]=0;
    55     dfs(hhh,0);
    56     cout<<ans;
    57     return 0;
    58 }

     法二:树形dp

    对于每个节点我们要记录两个值:

    f1 [ i ] 表示以 i 为根的子树中,i 到叶子结点距离的最大值

    f2 [ i ] 表示以 i 为根的子树中,i 到叶子结点距离的次大值

    对于一个节点,它到叶子结点距离的最大值和次大致所经过的路径肯定是不一样

    这样做就是,先看能否更新最大值,若能,它的次大值就是原先的最大值,再更新它的最大值;若不能,就看能不能更新次大值,若能,就更新,不能就不管它

    这样的话,最后的答案 answer = max { f1 [ i ] + f2 [ i ] }

    为什么这样做是正确的呢?

    我们跑一遍样例

    我们可以发现前两个样例是做

    的树的直径,以4为根最大值为20,次大值为2

    第三个样例是做以1为根的树上直径

    此时以1为根的最大值的链为23,然而因为这时f2[1]没有这个值,所以这时f2[1]为0,此时ans就从4的链转到了1的链来了

    之后就依次类推……

    code:

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 const int N=1e6+10;
     4 int n,m,tot,ans;
     5 int f1[N],f2[N];
     6 int head[N],ver[N],edge[N],nxt[N];
     7 char ch;
     8 void add(int x,int y,int z)
     9 {
    10     tot++;
    11     nxt[tot]=head[x];
    12     head[x]=tot;
    13     ver[tot]=y;
    14     edge[tot]=z;
    15 }
    16 void dp(int x,int fa)
    17 {
    18     for(int i=head[x];i;i=nxt[i])
    19     {
    20         int y=ver[i];
    21         if(y==fa)
    22           continue;
    23         dp(y,x);
    24         if(f1[x]<f1[y]+edge[i])
    25         {
    26             f2[x]=f1[x];
    27             f1[x]=f1[y]+edge[i];
    28         //    cout<<"f1[x]= "<<f1[x]<<" f2[x]= "<<f2[x]<<" edge[i]= "<<edge[i]<<" x= "<<x<<" y= "<<y<<endl;
    29         }
    30         else if(f2[x]<f1[y]+edge[i])
    31           f2[x]=f1[y]+edge[i];
    32         ans=max(ans,f1[x]+f2[x]);
    33         //cout<<"f1[x]= "<<f1[x]<<" f2[x]= "<<f2[x]<<" ans= "<<ans<<" x= "<<x<<" y= "<<y<<" edge[i]= "<<edge[i]<<endl;
    34     }
    35 }
    36 int main()
    37 {
    38     scanf("%d%d",&n,&m);
    39     for(int i=1,x,y,z;i<=m;i++)
    40     {
    41         scanf("%d%d%d",&x,&y,&z);
    42         add(x,y,z);
    43         add(y,x,z);
    44         cin>>ch;
    45     }
    46     dp(1,0);
    47     printf("%d",ans);
    48     return 0;
    49 }
    50 /*
    51 7 6
    52 1 6 13 E
    53 6 3 9 E
    54 3 5 7 S
    55 4 1 3 N
    56 2 4 20 W
    57 4 7 2 S
    58 */
  • 相关阅读:
    find命令
    shell编程基础
    grep命令
    awk命令
    结对项目之需求分析与原型模型设计
    使用Git进行代码管理的心得
    软件工程的实践项目的自我目标
    第五次作业——团队项目——需求规格说明书
    调研android开发环境的发展演变
    结对编程总结
  • 原文地址:https://www.cnblogs.com/nlyzl/p/11745475.html
Copyright © 2011-2022 走看看