zoukankan      html  css  js  c++  java
  • 树的重心以及直径算法

      树是一种很牛X的数据结构,性质有很多,比如(自行百度去)

      今天主要讲一下如何求树的重心和直径(在某些算法中要用到)

      1.树的重心

    树的重心也叫树的质心。找到一个点,其所有的子树中最大的子树节点数最少,那么这个点就是这棵树的重心,删去重心后,生成的多棵树尽可能平衡(百度百科)

      树的重心在点分治中可以保证递归的次数为log级别,保证代码不被卡。

      求树的重心主要是利用了DP的思想,size[x]表示以i点为根节点的子树的节点数,f[x]表示以x为根节点时,节点最多的子树节点数是多少

      注意最后f[x]要在f[x]与sum-size[x]中去max(sum是节点总数)

      刚开始任选一个点,递归更新即可

      ·一道板子题 POJ 1655 CODE

    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int N=20005;
    struct edge
    {
        int to,next;
    }e[N*2];
    int head[N],t,n,i,x,y,k,size[N],f[N],root,MIN;
    inline void read(int &x)
    {
        x=0; char ch=getchar();
        while (ch<'0'||ch>'9') ch=getchar();
        while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    }
    inline void write(int x)
    {
        if (x/10) write(x/10);
        putchar(x%10+'0');
    }
    inline void add(int x,int y)
    {
        e[++k].to=y; e[k].next=head[x]; head[x]=k;
    }
    inline int max(int a,int b)
    {
        return a>b?a:b;
    }
    inline void getroot(int now,int fa)
    {
        size[now]=1; f[now]=0;
        for (int i=head[now];i!=-1;i=e[i].next)
        if (e[i].to!=fa)
        {
            getroot(e[i].to,now);
            size[now]+=size[e[i].to];
            f[now]=max(f[now],size[e[i].to]);
        }
        f[now]=max(f[now],n-size[now]);
        if (f[now]<MIN) root=now,MIN=f[now];
        if (f[now]==MIN&&now<root) root=now;
    }
    int main()
    {
        read(t);
        while (t--)
        {
            k=root=0; MIN=1e9;
            memset(e,-1,sizeof(e));
            memset(head,-1,sizeof(head));
            read(n);
            for (i=1;i<n;++i)
            {
                read(x); read(y);
                add(x,y); add(y,x);
            }
            getroot(1,-1);
            write(root); putchar(' '); write(MIN); putchar('
    ');
        }
        return 0;
    }

      2.树的直径,就是带权树上距离最远的两点

      求法很简单,先任选一点做一次BFS,找出与它距离最远的点,那么该点就是树的直径的一个端点

      再以此端点为起点再做一次BFS,找出与它距离最远的点,那么另一个点就是树的直径的另一个端点,它们之间的距离就是树的直径

    1    设u为s-t路径上的一点,结论显然成立,否则设搜到的最远点为T则

    dis(u,T) >dis(u,s)     且  dis(u,T)>dis(u,t)   则最长路不是s-t了,与假设矛盾

    2   设u不为s-t路径上的点

        首先明确,假如u走到了s-t路径上的一点,那么接下来的路径肯定都在s-t上了,而且终点为s或t,在1中已经证明过了

        所以现在又有两种情况了:

        1:u走到了s-t路径上的某点,假设为X,最后肯定走到某个端点,假设是t ,则路径总长度为dis(u,X)+dis(X,t)

        2:u走到最远点的路径u-T与s-t无交点,则dis(u-T) >dis(u,X)+dis(X,t);显然,如果这个式子成立,

        则dis(u,T)+dis(s,X)+dis(u,X)>dis(s,X)+dis(X,t)=dis(s,t)最长路不是s-t矛盾

     (摘于CSDN dalao @大肥Fcx

      POJ 2631 树的直径裸题 CODE

    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int N=10005;
    struct egde
    {
        int to,next,v;
    }e[N];
    int head[N],q[N],dis[N],x,y,z,k,ans;
    bool vis[N];
    inline void add(int x,int y,int z)
    {
        e[++k].to=y; e[k].v=z; e[k].next=head[x]; head[x]=k;
    }
    inline int BFS(int now)
    {
        memset(vis,0,sizeof(vis));
        int H=0,T=1,num;
        vis[now]=1; dis[now]=0; q[1]=now; ans=-1;
        while (H<T)
        {
            int x=q[++H];
            if (dis[x]>ans) ans=dis[x],num=x;
            for (int i=head[x];i!=-1;i=e[i].next)
            if (!vis[e[i].to])
            {
                vis[e[i].to]=1;
                dis[e[i].to]=dis[x]+e[i].v;
                q[++T]=e[i].to;
            }
        }
        return num;
    }
    int main()
    {
        //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
        memset(head,-1,sizeof(head));
        memset(e,-1,sizeof(e));
        while (scanf("%d%d%d",&x,&y,&z)!=EOF)
        add(x,y,z),add(y,x,z);
        BFS(BFS(1));
        printf("%d",ans);
        return 0;
    }
  • 相关阅读:
    LeetCode(Weekly Contest 187)题解
    2021 暑期实习招聘思之未雨绸缪
    LeetCode(Weekly Contest 185)题解
    构建私有的 docker registry
    LeetCode(Weekly Contest 182)题解
    Win10图标变白
    远程连接linux上的mysql
    IDEA复制粘贴html文件打不进war包问题
    Git命令
    数据源收集
  • 原文地址:https://www.cnblogs.com/cjjsb/p/8482365.html
Copyright © 2011-2022 走看看