zoukankan      html  css  js  c++  java
  • [NOI2003][树的直径]逃学的小孩

    题目

    原题链接

    解说

    分享题说实话最开始都没什么思路,之前的大部分基本都看了一下题解启发思路。

    但这道题我可以大声而骄傲地说:它  是  我  完  完  全  全  自  己  做  出  来  的  !

    当然,代价就是Vjudge上WA了5遍,洛谷上WA了1遍,坑杀了我一节半课的时间。

    那么,转到正题,思路是怎么样的呢?

    首先可见这个图是一棵树,我们需要找到三个点,暂时叫ABC吧。其中(A到B的距离+C到AB距离中较短的)这一值最小。

    (以下maxdis代表树的直径,maxv代表第一遍DFS找到的直径端点,End代表第二遍DFS找到的另一个端点,bj i代表点是否在直径上,ans i 以及之后的ans1 i 代表节点 i 到maxv的距离,ans2 i 代表节点 i 到End的距离,b代表最后的答案)

    很显然这属于树的直径题。用两遍DFS找出树的两个顶点……之后呢?我在这里第一次卡住了。

    由于万恶的样例带来的错觉,我觉得题目要求的点一定是在直径上。所以我就用了第三遍DFS,找出来了所有直径上的点,在第二遍DFS的时候顺带计算出ans  i,之后循环一遍找到答案就行。循环大概就是:

     for(int i=1;i<=n;i++) if(bj[i]==1&&i!=End&&i!=maxv) b=max(b,min(maxdis-ans[i],ans[i]));  

    然后就WA了。

    之后很长时间我就卡在了这个代码的框框里,因为我一直坚定地认为要求的点在直径上。卡了半天还是感谢洛谷提供的一组错误样例让我恍然大悟:

    我的C为什么一定要在直径上?!

    下面是洛谷给的样例:

    简洁明了但很有说服力。

    显然,树的直径是2-4或3-4。由于这两种没有差别下面就用2-4举例。

    如果按照之前的想法,C在直径上,那么我算出来的结果是4.但若我们选3作为C呢?

    2到4的距离+3到2,4距离中较短的=5。

    也就是说C不一定在直径上。

    噫,好了,全崩了。

    但是思路正确了应该就没什么问题了。我们在第二遍dfs的时候顺带算出各点到maxv的距离,之后再DFS一遍算出各点到End的距离,之后把各个点遍历一遍算min(ans1[i],ans2[i])+maxdis的最大值即可。

    代码

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<queue>
     4 #include<algorithm>
     5 #include<cstring>
     6 #include<cmath>
     7 using namespace std;
     8 typedef long long ll;
     9 const int maxn=200000+3;
    10 int n,m,tot,head[maxn],End,maxv;
    11 ll maxdis,ans1[maxn],ans2[maxn];
    12 struct node{
    13     int to,next;
    14     ll w;
    15 }e[2*maxn];
    16 void Add(int from,int to,ll w){
    17     e[tot].w=w;
    18     e[tot].to=to;
    19     e[tot].next=head[from];
    20     head[from]=tot;
    21     tot++;
    22 }
    23 void dfs1(int u,int f,ll dis){//找第一个端点 
    24     if(maxdis<dis) {
    25         maxv=u;
    26         maxdis=dis;
    27     }
    28     for(int i=head[u];i;i=e[i].next){
    29         int v=e[i].to;ll w=e[i].w;
    30         if(v==f) continue;
    31         dfs1(v,u,dis+w);
    32     }
    33 }
    34 void dfs2(int u,int f,ll dis){//找第二个端点 
    35     ans1[u]=dis;//顺带求出所有点到maxv的距离 
    36     if(maxdis<dis) {
    37         maxdis=dis;
    38         End=u;
    39     }
    40     for(int i=head[u];i;i=e[i].next){
    41         int v=e[i].to;ll w=e[i].w;
    42         if(v==f) continue;
    43         dfs2(v,u,dis+w);
    44     }
    45 }
    46 void dfs3(int u,int f,ll dis){//求所有点到End的距离 
    47     ans2[u]=dis;
    48     for(int i=head[u];i;i=e[i].next){
    49         int v=e[i].to;ll w=e[i].w;
    50         if(v==f) continue;
    51         dfs3(v,u,dis+w);
    52     }
    53 }
    54 int main(){
    55     int n,m;
    56     tot=1;
    57     cin>>n>>m;
    58     for(int i=1;i<=m;i++){
    59         int from,to;ll w;
    60         cin>>from>>to>>w;
    61         Add(from,to,w);
    62         Add(to,from,w);
    63     }
    64     dfs1(1,-1,0);
    65     maxdis=0;
    66     dfs2(maxv,-1,0);
    67     dfs3(End,-1,0);
    68     ll b=-1;
    69     for(int i=1;i<=n;i++) if(i!=End&&i!=maxv) b=max(b,min(ans1[i],ans2[i])+maxdis); 
    70     //找最佳点 
    71     cout<<b;
    72     return 0;
    73 }
    View Code

    幸甚至哉,歌以咏志。

  • 相关阅读:
    fs.mkdir
    Node Buffer 利用 slice + indexOf 生成 split 方法
    class 类
    Proxy + Reflect 实现 响应的数据变化
    ivew 封装删除 对话框
    php调用js变量
    JS调用PHP 和 PHP调用JS的方法举例
    curl远程传输工具
    php 正则只保留 汉字 字母 数字
    php 发送与接收流文件
  • 原文地址:https://www.cnblogs.com/DarthVictor/p/12663252.html
Copyright © 2011-2022 走看看