分治算法

点分治

荒凉一梦 提交于 2019-12-27 07:28:06
点分治 蒟蒻迟迟没法开点分治的坑,主要是因为最近找了几道点分治题,全都可以用 长链剖分 写,由于博主又懒又菜,所以点分治没有得到练习的机会。终于,最近安排了专题分享,最菜的chd捡了一个分治专题,不得不学学这些东西了。 举个简单的例子,我们对树上的路径有一些询问。我们考虑对于树上的任意一个点,它的子树中的路径可以分为两类:一种是 经过根节点的路径 ,一种是 不经过根节点的路径 。由于不经过根节点的路径可以递归处理,这样分治的思路就很明显了。对于每一个点的子树,可以选取一个点作为根节点,递归到当前层时,只处理过根节点的路径,然后递归处理我们选定的根节点的儿子,就可以考虑到所有路径。 但是需要考虑一种极端情况:假如题目给出了一条长度为 \(n\) 的链,而我们第 \(i\) 层递归选定第 \(i\) 个点作为根节点往下递归,那就要递归 \(n\) 层,总的复杂度无法保证。这时我们就要考虑选取根节点的技巧了,每次递归选取 树的重心 为根节点,由于重心有一个性质:删除重心后得到的森林中的每一棵树的大小都不超过原树的一半,这样就能保证递归层数是 \(logn\) 层。 那么怎样求重心呢?考虑树的重心定义为一棵树中删除它后能使得到的森林中最大的树最小的点。一遍dfs就能求出,具体的实现下面会有。 接下来看题吧。 P4178 Tree 做这道题之前可以先看看它的弱化版 CF161D

【算法】点分治初探

爱⌒轻易说出口 提交于 2019-12-27 07:23:17
参考: http://blog.csdn.net/nixinyis/article/details/65445466 【简介】   点分治是一类用于处理树上路径点权统计问题的算法, 其利用重心的性质降低复杂度。 【什么是重心】   某个其所有的子树中最大的子树节点数最少的点被称为重心,删去重心后,生成的多棵树尽可能平衡。 【重心的性质】   ①重心其所有子树的大小都不超过$\frac{n}{2}$。   ②树中所有点到某个点的距离和中,到树的重心的距离和是最小的,如果有两个重心,那么到它们的距离和相同。   ③把两棵树通过两个点相连得到一棵新的树,新的树的重心必定在连接两棵树的重心的路径上。   ④一棵树添加或删除一个节点,树的重心最多会移动一条边的位置。   点分治的复杂度基于重心的第一个性质。 【点分治】   点分治是对于每一棵子树,都求出它的重心,并且以重心为根跑一遍这棵子树并统计经过重心的路径,因为我们知道重心所有子树的大小都只有原树的一半,也就是我们这么做最多只会递归$logn$层,若一层的复杂度$O(f(n))$,则总的时间复杂度为$O(f(n)logn)$。   接下来以bzoj1468: Tree为例题讲一下点分治。   对于这道题,显然用上方说的方法,对于每一个子树求出dep,排序后两端指针往中间靠拢统计即可。但是可能会统计重复,如下图

点分治学习笔记

邮差的信 提交于 2019-12-27 06:38:16
点分治 关于点分治,其实思想是非常好理解的,类比在数列上或是在平面上的分治算法(如归并排序,平面最近点对等),我们可以从字面上理解该算法: 以一个点为界限,将一棵树分成若干个子树,当划分到一定规模,就对每个子树分别进行求解 感性理解就好了 感受一个算法最直观的办法,就是来看一道模板题。 【模板】 点分治 给定一棵有 \(n\) 个点的树,询问树上长度为 \(k\) 的链是否存在。 首先可以很直观的知道,对于树上的任意一个点,有很多条经过它的链 那么,对于本题,我们是否可以在能够接受的时间内对这些经过该点的链进行求解呢? 答案是肯定的,只需要以该节点为根节点,对整颗树进行一遍 \(\text{DFS}\) ,求出各个点到该点的距离,然后就可以用桶排等方法解决该问题。 那么对于剩下的没有被处理到的链呢? 自然,我们可以以这个点,将整棵树断掉,将它的子树分开递归分治求解,这样这道题目就解决啦! 咳咳,真的这么简单吗? 我们来看一张图 多么优雅的一条链! 如果我们一开始以 \(1\) 为根节点,按照这个思路,我们需要进行 \(n\) 次操作,这样肯定是不行的。 也就是说,我们需要找到一个节点,使得在将其断掉之后, 剩下的各个子树的大小相对均匀 ,这样在进行分治求解的时候就可以让时间复杂度最优。 所以这里需要引入一个新的概念: 树的重心 定义:树的重心也叫树的质心。找到一个点

【学习】点分治

元气小坏坏 提交于 2019-12-27 06:37:42
点分治是一种基于分治的算法 整体思想为不断删根把一棵较大的树拆成n个小树再分别求解再合并 关于此题 我们先随意指定一个根,树上路径就分成了过根的和不过根在一个子树里的 这样经过根的路径即为dis[u]+dis[v],dis[i]是i到根的路径长度 不经过根的就再找这棵子树的根如此递归 显然分治 把一个无根树转化为有根树,找重心 void getrt(int u,int fa){//找根 sz[u]=1;maxp[u]=0; for(int i=head[u];i;i=e[i].nxt){ int v=e[i].to; if(v==fa||vis[v])continue; getrt(v,u); sz[u]+=sz[v]; maxp[u]=max(maxp[u],sz[v]); } maxp[u]=max(maxp[u],sum-sz[u]); if(maxp[u]<maxp[rt])rt=u; } 找重心的意义在于每次选取子树的重心为子树的树根进行处理, 这样总的递归深度不会超过 l o g N层 以保证复杂度 预处理 void calc(int u){ int p=0; for(int i=head[u];i;i=e[i].nxt){ int v=e[i].to; if(vis[v])continue; rem[0]=0; dis[v]=e[i].w; getdis(v,u);/

算法之分治

限于喜欢 提交于 2019-12-23 18:55:06
查看sogou和百度的词条解释,真是无言。 分治法的精髓: 分--将问题分解为规模更小的子问题; 治--将这些规模更小的子问题逐个击破; 合--将已解决的子问题合并,最终得出“母”问题的解; 看到分就感觉到这哪里是什么精髓,又看到治这个解释完全无言。去除了关键点,只能叫简,不能叫精髓。 查看算法导论解释。发现最精髓的红色部分,被词条完美的避过,真牛。 典型的分治:归并排序,分为小问题排序,再吧小排序合并为更大的排序,直到排完。 分:将问题划分为子问题, 子问题的形式与元问题一样,只是规模更小 。 治:递归求解出子问题, 如果子问题的规模足够小,则停止递归,直接求解。 合:将问题的解组合成原问题的解。 所以分治就是,必须分解大问题。如果分解后是另外一个问题。那没有任何用处,必须是形式一直,规模更小。如果规模可以无限小下去。也没有意义。必须小到一个时候,是一个很简单的问题,是可以解决的。那么就可以倒推到最先的大问题是可解决的。 分治常见算法 各种 常见排序 ,一般都是分治思想。 递归思路 , 天生分治思想。 来源: https://www.cnblogs.com/lsfv/p/10283813.html

算法分治之金块问题

↘锁芯ラ 提交于 2019-12-20 03:57:54
金块问题 分治算法——分治算法的基本思想(分-治-合) 用递归设计分治算法的基本步骤(基准与递归方程) 老板有一袋金块(共n块,n是2的幂(n≥2)),最优秀的雇员得到其中最重的一块,最差的雇员得到其中最轻的一块。假设有一台比较重量的仪器,希望用最少的比较次数找出最重和最轻的金块。并对自己的程序进行复杂性分析。 #include<stdio.h> //比较重量大小的函数 float min(float x,float y) { if(x<y) return x; else return y; } float max(float x,float y) { if(x>y) return x; else return y; } float Find_min(float A[],int left,int right) //这里是寻找最轻的金块的函数 { float la,ma,ra; if(left==right) //对于n=1的情况 { float min; min=A[right]; return min; } if(right-left==1.0) //对于n=2的情况 { la=A[left]; ra=A[right]; return(min(la,ra)); } if(right-left>1.0) //对于n>2的情况 { ma=(left+right)/2.0; la

算法学习之分治、迭代、递归

五迷三道 提交于 2019-12-11 17:57:26
分治策略:将一个大的问题,分解为相对独立的小问题,通过解决这些小问题得到的答案来拼凑出最后的解。 迭代:从小规模的问题,逐步累积出较大问题的解,最终获得所需要解决问题的解 递归:通过不断调用自身,但是相对的每次调用自己时候,所解决的问题规模较小,比较符合分治的思想。 来源: CSDN 作者: qq_24605545 链接: https://blog.csdn.net/qq_24605545/article/details/103484742

【分治算法】-3.为运算表达式设计优先级

北慕城南 提交于 2019-12-10 08:13:20
241. 为运算表达式设计优先级 给定一个含有数字和运算符的字符串,为表达式添加括号,改变其运算优先级以求出不同的结果。你需要给出所有可能的组合的结果。有效的运算符号包含 +, - 以及 * 。 示例 1: 输入: "2-1-1" 输出: [0, 2] 解释: ((2-1)-1) = 0 (2-(1-1)) = 2 示例 2: 输入: "2*3-4*5" 输出: [-34, -14, -10, -10, 10] 解释: (2*(3-(4*5))) = -34 ((2*3)-(4*5)) = -14 ((2*(3-4))*5) = -10 (2*((3-4)*5)) = -10 (((2*3)-4)*5) = 10 来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/different-ways-to-add-parentheses 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 思路: 分治 递归解 然后我们来进行分治算法三步走: 分解:按运算符分成左右两部分,分别求解 解决:实现一个递归函数,输入算式,返回算式解 合并:根据运算符合并左右两部分的解,得出最终解 解答来源自: https://www.cnblogs.com/silentteller/p/10897311.html

海量数据解决方案~

社会主义新天地 提交于 2019-12-08 18:51:34
题目 问题一:现有海量日志数据,要提取出某日访问百度次数最多的那个IP(可以将题干简化,假设日志中仅包含IP数据,也就是说待处理的文件中包含且仅包含全部的访问IP,但内存空间有限,不能全部加载,假设只有512MB) 解决方案: 这是一道典型的分治思想的题目,这种问题处理起来套路比较固定,对于大部分的数据量比较大的前提的问题而言,分治都是一个可选的解决方案,但不一定是最优的,解决方法基本划分为三步走: 第一:如何有效的划分数据 第二:如何在子集上解决问题 第三:如何合并结果 那么对于本问题就显得比较明显了: 首先解决如何划分,由于IP地地址范围从000.000.000.000~255.255.255.255共有2^32个大约4GB,那么我们可以通过取模的方式进行划分,直接将四段数据相连就得到一个IP对应的数字A,再对A模1024(模数取多少可以自己指定,保证每个小文件能被放到内存就好),这样大文件被划分成小文件了,并且相同的IP一定被划分到相同的文件中。 其次解决每个小文件中TOP1的问题: 这里可以用很多方式进行处理,比如你可以构造自己的HashMap,key为IP,value为当前出现次数,最后找到value最大的Key即为当前文件中出现次数最多的IP。 最后要解决结果合并问题: 这里直接将1024个子文件的统计结果进行比较就好,不用排序,直接选择最大的一个就好。 注意

海量数据topk问题

拜拜、爱过 提交于 2019-12-08 18:39:01
常见问题: ①Top K问题:分治+Trie树/Hash_map+小顶堆。采用Hash(x)%M将原文件分割成小文件,如果小文件太大则继续Hash分割,直至可以放入内存。 ②重复问题:BitMap位图 或 Bloom Filter布隆过滤器 或 Hash_set集合。每个元素对应一个bit处理。 ③排序问题:外排序 或 BitMap位图。分割文件+文件内排序+文件之间归并。 Top K问题: 有一个1G大小的一个文件,里面每一行是一个词,词的大小不超过16字节,内存限制大小是1M。返回频数最高的100个词。 ①分治:顺序读文件,对每个词x取Hash(x)%2000,按照该值存到2000个小文件中。每个文件是500k左右。如果有文件超过了1M则继续分割。O(N) ②Trie树/Hash_map:字符串用Trie树最好。对每个小文件,统计其中出现的词频。O(N)*(平均字符长度),长度一般是常数,也就是O(N). ③小顶堆:用容量为100的小顶堆,以频率为value值插入,取每个文件现频率最大的100个词,把这100个词及相应的频率存入文件。最差O(N) lg(100),也就是O(N).注:2,3步骤合起来需要一轮磁盘存取过程。存入文件的个数可以缩减一下,因为主要开销在磁盘读取上,减少文件读取次数,可以在每个文件存取最大容量的字符数量,比如这道题1 (M/16字节字符串长度+频率