《算法笔记》学习笔记(10):二分

最经典的二分查找不再赘述

#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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值