zoukankan      html  css  js  c++  java
  • 详解点分治

    详解点分治

    本篇随笔讲解算法竞赛中的点分治淀粉质算法。


    一、点分治概述及应用

    点分治是一种在树上进行路径静态统计的算法。

    所谓树上的静态统计,并不是像树剖一样维护路径最值、路径和之类的统计。那样的话,这个算法的存在就没什么意义了。

    来上道小题体会一下点分治的解决问题。

    给定一棵有n个节点的带边权无根树。求长度不超过k的路径有多少条。

    看懂了么?就是这种有限制的路径统计问题,非常适合用点分治解决。


    二、点分治的基本实现思路

    点分治点分治,最终还是落回分治这个东西。分治是什么?其原理就是把原问题拆解成几个子问题,分别求解子问题之后再合并出原问题。那么点分治其实就是对一棵树进行拆开,分治。

    那么用上面的问题作为引入。

    显然,暴力的方式是枚举所有路径,然后判断是否合法。但是所有的路径都可以分两种情况:第一种,过根节点。第二种,不过根节点。

    那么我们把根节点断掉,又形成了很多新子树,对于这些子树,仍然会对它们的路径分这两种情况......

    可以看出,所有不过整棵树根节点的路径,必会在这种不断拆解形成新子树的过程中,过一遍根节点。

    对其有感觉么?这就是递归的过程么。

    于是点分治实现的大致思路就是:

    1、任取一个点作为无根树根节点。

    2、计算所有第一种路径。

    3、将当前根节点断掉,递归计算下一层子树。步骤同上。


    三、点分治的时空复杂度分析

    点分治过程中,分治点的选择至关重要。

    一个递归层的时间复杂度是(O(nlog n))

    极端情况下,树会退化成链,那么对于链,我们最不划算的选择分治点方式是每次选择链头,那么就会变成递归n次,总复杂度是(O(n^2log n))

    那么我们每次选择链的中心呢?

    很简单,递归变成了log次,总复杂度就是(O(nlog^2 n))

    那么,对于链是这样选择,对于树也是这样选择。选”中点“当然是最划算的。那么树的“中点”是什么呢?

    即,树的重心。

    重心有一个性质:那就是删除重心后,树被拆成若干个部分,这些部分中,不会有任何一个部分超过总点数的二分之一。否则就不符合重心的性质。

    所以,每次选择重心作为分治点,就保证了整个点分治算法的时间是(O(nlog^2 n))


    四、点分治的代码实现

    根据第二部分的思路概述,我们应该很容易用递归方式写出其框架代码:

    void dfz(int x)//大多数人叫divide,我就喜欢这样
    {
    	v[x]=1;//避免重复选择绕死
        calc();//注意!点分治的精华!
        for(int i=head[x];i;i=nxt[i])
        {
            int y=to[i];
            if(v[y])
            	continue;
           	sum=size[y],root=0;//找子树重心的前置信息
            getroot(y,0);//这波是找子树重心
            dfz(root);//套娃递归
        }
    }
    

    其中的getroot函数,功能是找子树重心。根据重心的定义及之前学过的求法,应该这么写:

    void getroot(int x,int f)
    {
    	size[x]=1,mp[x]=0;
        for(int i=head[x];i;i=nxt[i])
        {
            int y=to[i];
            if(y==f||v[y])
                continue;
            getroot(y,x);
            size[x]+=size[y];
            mp[x]=max(mp[x],size[y]);
        }
        mp[x]=max(mp[x],sum-size[y]);
        if(mp[x]<mp[root])
            root=x;
    }
    

    只有这两个能算作模板化的东西。

    然而点分治的精华,calc函数部分,需要按题目的意思来求。其功能就是维护题目想要维护的信息。

    这个就需要自己通过例题和练习细细体会。

  • 相关阅读:
    静态绑定与动态绑定
    Java之浅拷贝与深拷贝
    python类对象及类实例的创建过程
    以订单和商品为例进行详细的组内数据获取的分析
    根据给定时间及偏移的年份求偏移后时间的前一天(支持偏移量为正和负)
    mysql取到组内的前几条数据
    python进程通信的几种实现方式
    python-redis之数据类型二
    python-redis之数据类型
    python-redis
  • 原文地址:https://www.cnblogs.com/fusiwei/p/13822466.html
Copyright © 2011-2022 走看看