最经典的二分查找不再赘述
#define _CRT_SECURE_NO_WARNINGS 1
#include<cstdio>
using namespace std;
//二分查找
int binarysearch(int A[], int left,int right, int x)
{
int mid;
while (left <= right)
{
mid = (left + right) / 2;
if (A[mid] == x)return mid;
else if (A[mid] > x)
{
right = mid - 1;
}
else
{
left = mid + 1;
}
}
return -1;
}
二分法拓展
计算sqrt(2)的近似值:
令left=1,right=2,根据mid出f(x)的值与2的大小来选择子区间进行逼近。
示例程序
#define _CRT_SECURE_NO_WARNINGS 1
#include<cstdio>
const double eps = 1e-5;//精度
//计算平方
double f(double x)
{
return x * x;
}
double calSqrt()
{
double left = 1, right = 2, mid;
while (right - left > eps)
{
mid = (left + right) / 2;
if (f(mid) > 2)
{
right = mid;
}
else
{
left = mid;
}
}
return mid;
}
int main()
{
printf("%lf\n",calSqrt());
return 0;
}
运行结果
装水问题
有一个侧面看去是半圆的储水装置,该半圆的半径为R,要求往里面装入高度为h的水,使其在侧面看去的面积与半圆面积的比例恰好为r,如下图所示。现在给定R和r,求高度h。
在范围[0,R]内进行二分直到满足比例r为止。
圆的面积πR^2
扇形面积 = (n/360)πR²
示例程序
//装水问题
const double PI = acos(-1.0);//定义PI
const double eps = 1e-5;//精度
double f(double R, double h)//计算r=f(h)
{
double alpha = 2 * acos((R - h) / R);
double L = 2 * sqrt(R * R - (R - h) * (R - h));
double S1 = alpha * R * R / 2 - L * (R - h) / 2;
double S2 = PI * R * R / 2;
return S1 / S2;
}
double solve(double R, double r)
{
double left = 0, right = R, mid;
while (right - left > eps)
{
mid = (left + right) / 2;
if (f(R, mid) > r)
{
right = mid;
}
else
{
left = mid;
}
}
return mid;
}
int main()
{
double R, r;
scanf("%lf %lf", &R, &r);
printf("%.4f\n", solve(R, r));
return 0;
}
运行结果
木棒切割问题
给出N根木棒,长度均已知,现在希望通过切割它们来得到至少K段长度相等的木棒(长度必须为整数),问这些长度相等的木棒最长能有多长。
对于该问题,如果长度相等的木棒的长度L越长,那么可以得到的木棒段数k越少。从这个角度出发,我们便可以想到运用二分法的思想,根据对当前长度L来说能得到的木棒段数k与K的大小关系来进行二分。由于这个问题可以写成求解最后一个满足条件“k>=K”的长度L,因不妨转换为求解第一个满足“k<K”的长度L,然后减1即可。
示例程序
//木棒切割问题
const int maxn = 10010;
int len[maxn] = { 10, 24, 16 }; //每根木棒的长度
int N = 3; //木棒的个数
int K = 7; //需要切割得到的最少的木棒的段数
//计算每段切割的长度为mid时对应的木棒段数k
int calc_num(int L) {
int num = 0;
for (int i = 0; i < N; i++) {
num += len[i] / L;
}
return num;
}
//二分区间为[left, right],传入值为[0,n]
//计算k>K的区间。
int solve(int left, int right) {
int mid;
while (left + 1 < right) { //左开右闭区间,注意修改
mid = (left + right) / 2;
if (calc_num(mid) < K) {
right = mid;
}
else {
left = mid; //注意修改
}
}
return right;
}
int main() {
sort(len, len + N);
int L = solve(0, len[N - 1] + 1) - 1; //注意修改
printf("L = %d\n", L);
for (int i = 0; i < N; i++) {
printf("%d => %d * %d\n", len[i], len[i] / L, L);
}
return 0;
}
运行结果
快速幂
先来看一个问题:
给定三个正整数a,b,m,求ab%m
在a,b较大时,显然不能直接相乘,会造成溢出,因此要使用快速幂的做法
1.若b为奇数,那么有ab=a*ab-1
2.若b为偶数,那么有ab=ab/2*ab/2
由此可以写出快速幂的递归写法
示例程序
typedef long long LL;
LL binaryPow1(LL a, LL b, LL m)
{
if (b == 0)return 1;//b为0时,a^0=1
if (b % 2 == 1)return a * binaryPow1(a, b - 1, m) % m;//b为奇数时,转换为b-1
else//b为偶数时,转换为b/2
{
LL mul = binaryPow1(a, b / 2, m);
return mul * mul % m;
}
}
其中,if (b % 2 == 1)可以用if(b&1)代替
同时,n为偶数时,应先计算出binaryPow1(a, b / 2, m),再相乘。
快速幂的迭代写法
对ab来说,如果把b写成二进制,那么b就可以写成若干二次幂之和
如13=23+22+20,那么a13=a8+4+1=a8*a4*a1
由此我们可以写出代码
LL binaryPow2(LL a, LL b, LL m)
{
LL ans = 1;
while (b > 0)
{
if (b & 1)
{
ans = ans * a % m;
}
a = a * a % m;
b >>= 1;
}
return ans;
}