二分法使用条件:序列有序
1.二分查找
- 先确定待查记录所在范围,然后逐步缩小范围直到找到或找不到该记录为止
int Search_Bin(int ST[],int key)
{
int low = 0,high = ST.length-1;
while(low<=high)
{
int mid = (low+high)/2;
if(ST[mid]<key)
high = mid - 1;
else if(ST[mid]>key)
low = mid +1;
else
return mid;
}
return -1;
}
2.二分法扩展
上面讲解的都是整数情况下的二分查询问题,事实上二分法的应用远不止此,下面看几个相关的例子
计算√2的近似值
对于f(x)=x2来说,在x∈[1,2]范围内,f(x)是随着x的增大而增大的,这就给使用二分法创造了条件,当然由于√2是无理数,只能求到近似值,这里不妨以精确到10-5为例
#define MIN 1e-5
int main()
{
double low = 1;
double high = 2;
double mid = 0 ;
while (high-low>MIN)
{
mid = (low + high) / 2;
if (pow(mid, 2) > 2)
high = mid;
else
low = mid;
}
cout << mid;
return 0;
}
事实上,计算√2的近似的问题其实是这样一个问题的特例:给定一个定义在[L,R]上的但单调函数f(x),求方程f(x) = 0的根。
木棒切割问题
给出N根木棒,长度均已知,现在希望通过切割它们来得到至少K段长度相等的木棒(长度必须是整数),问这些长度相等的木棒最长能有多长。
首先木棒最长不能长于其中最短的那个木棒的长度,我们可以根据最长长度进行二分查找,找到可以分割要求的K段。
int getK(int stick[], int n,int L)
{
int count = 0;
for (int i = 0; i < n; ++i)
{
count += stick[i] / L;
}
return count;
}
int main()
{
int N,K;
int stick[10];
cin >> N >> K;
int min = INT_MAX;
for (int i = 0; i < N; ++i)
{
cin >> stick[i];
if (stick[i] < min)
min = stick[i];
}
int low = 1, high = min,mid=0;
while (low <= high)
{
mid = (low + high) / 2;
if (getK(stick, N, mid) > K)
low = mid+1;
else
high = mid-1;
}
cout << mid;
}
快速幂(二分幂)
先来看一个问题:
给定三个正整数a、b、m(a<109,b<106,1<m<109),求ab%m
可以看到,如果直接用循环写该题的话时间复杂度为O(b),显然超时,所以我们需要了解快速幂(二分幂)
快速幂基于以下事实:
- 如果b是奇数,那么有ab = a*ab-1
- 如果b是偶数,那么有ab = ab/2*ab/2
显然,b是奇数的情况总可以在下一步转换为b是偶数的情况,而b是偶数的情况总可以在下一步转换为b/2的情况,这样,在log(b)级别次数的转换后,就可以把b变为0,而任何正整数的0次方都是1
这显然是递归的思想,于是可以得到快速幂的递归写法,时间复杂度为O(logb)
int binaryPow(int a, int b)
{
if (b == 0)
return 1;
if (b & 1)
return a * binaryPow(a, b - 1);
else
return binaryPow(a, b / 2) * binaryPow(a, b / 2);
}
int main()
{
int a, b;
cin >> a >> b;
cout<<binaryPow(a, b);
}