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;
//这里放要求的函数;
}
对于求解一些实际问题,当公式难以推导出来时,二分、三分法可以较为精确地求解出一些临界值,且效率也是令人满意的。
灵活应用这些方法对解题会很有帮助。