分治 二分答案 三分未完结

分治,字面上的解释是"分而治之",就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。分治法是很多高效算法的基础,如排序算法(快速排序,归并排序),傅立叶变换(快速傅立叶变换)等等。

分治三步法:

1 划分问题:把问题划分成元素个数尽量相等的两半;

2 递归求解:把两半元素分别求解;

3 合并问题:把两个已经求解的元素合并成一个;

二分的基本用途是在单调序列或单调函数中做查找操作,注意:二分一定是在一个单调有序的集合或函数中查找一个解;

整数定义域上的二分(模板):

int erfen(int l,int r){

  int l=1,r=n,ans;

  while(l<=r){

    int mid=(l+r)>>1;

    if(check(mid)) ans=mid,l=mid+1;

    else r=mid-1;

  }

  return ans;

}

注意,我们在二分实现中采用了右移运算>>1,而不是整数除法/2;前者是向下取整,后者是向零取整;

实数域上的二分(模板):

double erfen(double l,double r){

  double mid;

  while(fabs(l-r)>eps){

    mid=(l+r)/2.0;

    if(check(mid)) r=mid;

    else l=mid;

  }  

  return l;

}

fabs是求实数绝对值的函数,注意与整数绝对值函数abs的不同;

eps是要确定好的精度,一般题目要求保留k位小数,则取eps=1e-(k+2);

有时精度不容易确定或表示,就干脆采用循环固定次数的二分方法,往往结果的精度更高(实数二分常常卡精度,爆精度);

for(int i=0;i<100;i++){

  double mid=(l+r)/2;

  if(check(mid)) r=mid;

  else l=mid;

}

二分答案:最小值最大(或最大值最小)的问题,常常选用二分法求解,同时配合贪心,DP等其他算法检验答案的合理性,将最优化问题转化为判定性问题;

二分查找:用具有单调性的布尔表达式求解分界点,比如在有序数列中求数字x的排名;

例题:数列分段II(洛谷1182)

  对于给定的一个长度为N的正整数数列A,现要将其分成M(M≤N)段,并要求每段连续,且每段和的最大值最小。

  求最大值最小,典型的二分题;

例题:扩散(洛谷1661)

  一个点每过一个单位时间就会向四个方向扩散一个距离,两个点a、b连通,记作e(a,b),当且仅当a、b的扩散区域有公共部分。连通块的定义是块内的任意两个点u、v都必定存在路径e(u,a0),e(a0,a1),…,e(ak,v)。给定平面上的n给点,问最早什么时刻它们形成一个连通块。

  方法一:二分时间t,运用并查集判断连通块;

  方法二:假设任意两点之间有边,相当于求所有点构成的最小生成树中最长的一条边。把两点扩散连接的时长作为边的权值,开一个结构体存边,然后用kruskal算法求最小生成树,找到其中最长的边即可。

例题:Best Cow Fences(POJ2018)

例题:[Usaco2005 feb]愤怒的牛(BZOJ1734)

例题:Innovative Business

离线分治算法:

基于时间的分治算法——CDQ分治

  我们不妨这样想,对于操作序列中的每个“查询”,计算其结果等价于计算“初始数据”以及“之前的所有修改”对该查询造成的影响;

  设一共有M项操作,任意l,r属于区间[1,M],l<=r,定义solve(l,r)为:任意k属于区间[l,r],若第k项操作是查询,则计算第l~k-1项操作中的修改对它造成的影响.计算方法如下:

    1设mid=(l+r)>>1,递归计算solve(l,mid);

    2 递归计算solve(mid+1,r);

    3 计算第l~mid项操作中的所有修改对第mid+1~r项操作中所有查询造成的影响;

  结合一道三维偏序的模板题,用CDQ分治算法求解(洛谷3810):

  有 n个元素,第 i个元素有 ai​、bici 三个属性,设 f(i)表示满足 ajai 且 bjbi 且 cjci 的 jj的数量。对于 d[0,n),求 f(i)=d 的数量;

  一维a属性结构体内嵌套比较函数sort排序;二维b属性CDQ分治;三维c属性树状数组;

  一开始a排序需要考虑a,b,c整体排序,然后分治里b排序要用≤,或者b,c整体≤,注意这里就不关a什么事了,分治已经保证用左边更新右边了;枚举完之后还要把树状数清空,但不可以使用memset,因为时间复杂度是n^2的,只能进行增加操作的逆操作;最后要进行判重(为什么要判重?因为如果不判重,然后出现了3个完全相同的属性,就会出现问题);

  例题:天使玩偶(洛谷4169 BZOJ2716)

基于值域的分治算法——整体分治

  直接结合模板题来讲解(洛谷SP3946 POJ2104)

  给定一个长度为N的整数序列A,执行M次操作,其中第i次操作给出三个整数l,r,k,求A的下标区间l~r中第k小的数。

  

猜你喜欢

转载自www.cnblogs.com/PPXppx/p/9827431.html