二分三分法总结

1 二分法,是一种降低DP复杂度的有效算法,二分查找用于在多条记录中快速找到待查找的记录。它的思想是:每次将查找的范围缩小一半,直到最后找到记录或者找不到记录返回!

2 简单定义:在一个单调有序(注意一定是有序)的集合中查找元素,每次将集合分为左右两部分,判断解在哪个部分中并调整集合上下界,重复直到找到目标元素。

3 二分查找的时间复杂度为O(logn)

算法:当数据量很大适宜采用该方法。采用二分法查找时,数据需是排好序的。

基本思想:假设数据是按升序排序的,对于给定值x,从序列的中间位置开始比较,如果当前位置值等于x,则查找成功;若x小于当前位置值,则在数列的前半段中查找;若x大于当前位置值则在数列的后半段中继续查找,直到找到为止。

算法如下:

1.确定查找范围front=0,end=N-1,计算中项mid(front+end)/2。

2.若a[mid]=x或front>=end,则结束查找;否则,向下继续。

3.若a[mid]<x,说明待查找的元素值只可能在比中项元素大的范围内,则把mid+1的值赋给front,并重新计算mid,转去执行步骤2;若a[mid]>x,说明待查找的元素值只可能在比中项元素小的范围内,则把mid-1的值赋给end,并重新计算mid,转去执行步骤2

4 二分法的归和非递归算法如下:

(1)非递归:

nt binary_search(int arr[],int n ,int key){  
    int mid;  
    int low = 0,high = n-1;  
    while(low <= high){  
        mid =  (high + low)/2;   //中间元素,防止溢出  
        if(key == arr[mid]) return mid;//找到时返回  
        else if(key > arr[mid]){  
            low = mid + 1;//在更高的区间搜索  
        }else{  
            high = mid - 1;//在更低的区间搜索  
        }  
    }  
    return -1;//没有找到元素,返

(2)递归:

int binary_search(int arr[],int low,int high,int key){  
    if(low > high) return -1;  
    int mid =  (high + low)/2   //中间元素,防止溢出  
    if(key == arr[mid])return mid;//找到时返回  
    else if(key > arr[mid]){  
        return binary_search(arr,mid + 1,high,key);/在更高的区间搜索  
    }else{  
        return binary_search(arr,low,mid - 1,key);/在更低的区间搜索  
    }  
   }

常见拓展:

对于某些问题,如果答案具有特定的范围,并且验证答案是否成立的函数具有单调性。则可以在范围内对答案进行二分验证,从而快速确定答案

典型例题:

HDU 1551 Cable master
将n根网线切成k段相同长度的网线,问可切成的最长长度是多少;
思路:利用二分发查找答案,每次偏右查找(因为要查找大的)

关键代码:

int n,k;
double a[10005],sum;

int judge(double s)
{
    int cnt = 0;
    for(int i = 0; i<n; i++)
    {
        cnt+=(int)(a[i]/s);
    }
    if(cnt>=k) return 1;
    return 0;
}


int main()
{
    int i;
    while(~scanf("%d%d",&n,&k),n+k)
    {
        sum = 0;
        for(i = 0; i<n; i++)
        {
            scanf("%lf",&a[i]);
            sum+=a[i];
        }
        sum = sum/k;
        double l = 0,r = sum;
        while(fabs(l-r)>exp)
        {
            double mid = (l+r)/2;
            if(judge(mid))
                l = mid;
            else
                r = mid;
        }
        printf("%.2f\n",l);
    }

    return 0;
}


二、三分法

当需要求某凸性或凹形函数的极值,通过函数本身表达式并不容易求解时,就可以用三分法不断逼近求解。

类似二分的定义Left和Right
mid = (Left + Right) / 2
midmid = (mid + Right) / 2;
如果mid靠近极值点,则Right = midmid;
否则(即midmid靠近极值点),则Left = mid;


例子:ZOJ 3203 Light Bulb

如图,人左右走动,求影子L的最长长度。
根据图,很容易发现当灯,人的头部和墙角成一条直线时(假设此时人站在A点),此时的长度是影子全在地上的最长长度。当人再向右走时,影子开始投影到墙上,当人贴着墙,影子长度即为人的高度。所以当人从A点走到墙,函数是先递增再递减,为凸性函数,所以我们可以用三分法来求解。



题意是给定如图的H,h,D,求最大的影子L。
  
如图所示,设X,Y。
由三角形相似得,x / y = H / (y + D) 。
解得:y = x * D / (H - x)。
由三角形相似得,x / y = h / (y + L -x)。
带入y,解得 L = x + D * (h - x) / (H - x)。

核心代码:

double mid, midmid;
while ( low + eps < high )
{
    mid = (low + high) / 2;
    midmid = (mid + high ) / 2;
    double cmid = cal(mid);
    double cmidmid = cal(midmid);
    if ( cmid > cmidmid ) 
		    high = midmid;
    else 
        low = mid;
}
double cal(double x)
{
    return (h * D - H * x) / (D - x) + x;
    //这里放要求的函数;    
}

对于求解一些实际问题,当公式难以推导出来时,二分、三分法可以较为精确地求解出一些临界值,且效率也是令人满意的。
灵活应用这些方法对解题会很有帮助。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值