zoukankan      html  css  js  c++  java
  • NOIP2007 树网的核

    传送门

    最近搞一搞树型结构……毕竟自己树的知识学的太垃圾了。

    首先这道题非常明显要求树的直径。树的直径有好多好多种求法,这里我选择了一位dalao的非常简洁的dfs的方法。先看一下代码。

    void dfs(int x)
    {
        for(int i = head[x];i;i = e[i].next)
        {
        if(fa[x] == e[i].to || vis[e[i].to]) continue;
        fa[e[i].to] = x;
        dis[e[i].to] = dis[x] + e[i].v;
        dfs(e[i].to);
        }
    }

    代码非常的简短。具体的思想也很好理解,就是依次向下dfs,然后使用fa来记录在直径上都有哪些点。不过这个使用的时候要注意,就是每次要被开始进行搜索的那个点的dis赋成0.

    思想就是两遍dfs。第一遍先随便搜,搜到一个与之相距最远的点(这个点必然是树的直径之一),之后再从这个点进行dfs,之后搜到一个与之距离最远的点(直径的另一个端点),这就是树的直径了。

    然后怎么做?

    我们可以使用直径的性质。首先来看偏心距的定义,对于一条路径F,树中与之距离最远的点到路径F的距离为F的偏心距。首先每条直径肯定会有一个最小的偏心距,所以只要求一条直径就可以啦。

    之后,我们要求偏心距最小……首先我们通过直径的性质可以知道一件事,就是对于直径上一条路径的偏心距,一定是当前这条路径的两端点到直径两端点距离的最大值。这个必然成立,否则肯定存在一条比你当前求的直径还长的一条链。

    那么我们就可以使用贪心的思想,每次在直径上取一条长度不大于限制条件的路径,之后每次更新最小偏心距即可。注意有一种特殊情况就是直径的总长要小于限制的长度,这时就从每个在直径上的点开始dfs,最后输出最大的值就好啦。

    看一下代码。

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<iostream>
    #include<cmath>
    #include<queue>
    #include<set>
    #define rep(i,a,n) for(int i = a;i <= n;i++)
    #define per(i,n,a) for(int i = n;i >= a;i--)
    #define enter putchar('
    ')
    
    using namespace std;
    typedef long long ll;
    const int M = 5000005;
    const int INF = 1000000009;
    
    int read()
    {
        int ans = 0,op = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9')
        {
        if(ch == '-') op = -1;
        ch = getchar();
        }
        while(ch >= '0' && ch <= '9')
        {
        ans *= 10;
        ans += ch - '0';
        ch = getchar();
        }
        return ans * op;
    }
    
    struct node
    {
        int next,to,v;
    }e[M<<1];
    
    int n,s,x,y,ecnt,head[M],dis[M],fa[M],r1,r2,ans = INF,k,z;
    bool vis[M];
    
    void add(int x,int y,int z)
    {
        e[++ecnt].to = y;
        e[ecnt].v = z;
        e[ecnt].next = head[x];
        head[x] = ecnt;
    }
    
    void dfs(int x)
    {
        for(int i = head[x];i;i = e[i].next)
        {
          if(fa[x] == e[i].to || vis[e[i].to]) continue;
          fa[e[i].to] = x;
          dis[e[i].to] = dis[x] + e[i].v;
          dfs(e[i].to);
        }
    }
    void solve()
    {
        ans = -1;
        for(int i = r2;i;i = fa[i]) vis[i] = 1;
        for(int i = r2;i;i = fa[i])    dis[i] = 0,dfs(i);
        rep(i,1,n) ans = max(ans,dis[i]);
        printf("%d
    ",ans);
    }
    int main()
    {
        n = read(),s = read();
        rep(i,1,n-1) x = read(),y = read(),z = read(),add(x,y,z),add(y,x,z);
        dfs(1);
        memset(fa,0,sizeof(fa));
        rep(i,1,n) if(dis[i] > dis[r1]) r1 = i;//找到第一个端点
        dis[r1] = 0,dfs(r1);
        rep(i,1,n) if(dis[i] > dis[r2]) r2 = i;//找到第二个端点
        k = r2;
        if(dis[k] <= s)
        {
          solve();//直径长度小于限制长度的解决
          return 0;
        }
        for(int i = r2;i;i = fa[i])
        {
          while(fa[k] && dis[i] - dis[fa[k]] <= s) k = fa[k];//贪心取链
          ans = min(ans,max(dis[k],dis[r2] - dis[i]));//更新最小偏心距
        }
        printf("%d
    ",ans);
        return 0;
    }

     

  • 相关阅读:
    SOA简介
    WebService传XML
    架构设计师与SOA(转)
    第二次自己总结WebService
    SQL SERVER的数据类型
    使用.ashx文件处理IHttpHandler实现发送文本及二进制数据的方法
    写了一个分页存储过程把总记录数也写到返回表的每行了
    ASP.net新手经常会碰到的问题
    动态添加、删除附件
    七问七答 SOA
  • 原文地址:https://www.cnblogs.com/captain1/p/9636634.html
Copyright © 2011-2022 走看看