算法笔记二分查找题目
解决的问题
- 寻找有序序列中第一个满足条件的元素的位置
注意点
- 当二分上界超过int型数据范围的一半,那么当欲查询元素在序列较靠后的位置时。
mid=(left+right)/2
可能溢出,此时一般使用mid=left+(right-left)/2
避免溢出。
题目
如何在一个严格递增序列A中找出给定的数X
//如何在一个严格递增序列A中找出给定的数X
int binarySearch(int A[], int left,int right,int X)
{
//初始left=0 right=n-1
while (left <= right)
{
int mid = left + (right - left) / 2;
if (A[mid] == X)
{
return mid;
}
else if (A[mid] > X)//X应在mid的左侧
{
right = mid - 1;
}
else {//X应在mid的右侧
left = mid + 1;
}
}
//在合法区间中没有找到,返回-1
return -1;
}
如果递增序列A中的元素可能重复,那么如何对给定的欲查询元素x,求出序列中第一个大于等于x的元素的位置L以及第一个大于x的元素位置R。这样元素x在序列中的存在区间就是左闭右开区间[L,R)
计算根号2在误差为10-5的近似值
#define ens 1e-5
#include<algorithm>
#include<iostream>
using namespace std;
double f(double x)
{
return x * x;
}
//计算根号2在误差为10-5的近似值
//可以将题意变为在[1,2]之间寻找误差在这个范围之间的近似值,使用x^2逼近
double binary(double left,double right,int M)
{
//取[left,right]中间值,如果(mid+-end)^2>2=>mid>根号2 left=mid
while (right-left > ens)
{
double mid = left + (right - left) / 2;
if (f(mid)>=M)//说明在[left,mid]
{
right = mid;
}
else
{
left = mid;
}
}
return left;
}
int main()
{
double res = binary(1, 2, 2);
cout << res << endl;
}
给定一个定义在[L,R]上的单调函数f(x),求方程f(x)=0的根
#define eps 1e-5
//给定一个定义在[L,R]上的单调函数f(x),求方程f(x)=0的根
double f(double x)
{
return x * x - 2;
}
double getF(int L,int R)
{
int mid = L + (R - L) / 2;
while (R - L > eps)
{
if (f(mid) > 0)
{
R = mid;
}
else if (f(mid) == 0)
{
return mid;
}
else
{
L = mid;
}
}
return mid;
}
装水问题:有一个侧面看去时半圆的出水装置,该半圆的半径为R要求往里面装入高度为h的水,使其在侧面看去的面积与半圆面积的比例恰好为r。现在给定R和r求高度h
#include<cmath>
#include<cstdio>
#define eps 1e-5
#define PI acos(-1.0)
double f(double h, double R)
{
double alpha = 2 * acos((R - h) / R);
double L = 2 * sqrt(R * R - (R - h) * (R - h));
double S1 = alpha * R * R - L * (R - h);
double S2 = PI * R * R;
return S1 / S2;
}
//因为f随着h的增大而增大
double solve(double r,double R)
{
int left = 0, right = R;
int mid;
while (right - left > eps)
{
mid = left + (right - left) / 2;
if (f(mid, R) > r)
{
right = mid;
}
else {
left = mid;
}
}
return mid;
}
木棒切割问题:给出N根木棒,长度均已知,现在希望通过切割它们来得到至少K段长度相等的木棒(长度必须是整数)。问这些长度相等的木棒最长能有多长。
#include<iostream>
using namespace std;
int mubangs[] = { 10,24,15 };
int n=3;
int f(int length)
{
int num = 0;
for (int i = 0; i < n; i++)
{
int t = mubangs[i] / length;
num += t;
}
return num;
}
int getMaxL(int K)
{
int length = 1;
int num = f(length);
while (num>= K)
{
length++;
num = f(length);
}
return length-1;
}
给出N个线段的长度,试将它们头尾相接(顺序任意)地组合成一个凸多边形,使得该凸多边形的外接圆(即能使凸多边形的所有顶点都在圆周上的圆)的半径最大。
//外接圆圆心与每个线段顶点连接后会有一个圆心角,如果圆心在凸多边形内部,则所有圆心角之和应该为2π。如果圆心在凸多边形外部,则最大的圆心角等于其他圆心角之和。
//如果半径比rmax大,那么这些线段的圆心角之和sum<2∗π,线段没法首尾相连的在外接圆上,半径应变小。
//如果半径比rmax小,那么这些线段的圆心角之和sum>2∗π,这时构成的外接圆不是半径最大的外接圆,半径应变大。
//给出N根木棒,长度均已知,
//现在希望通过切割它们来得到至少K段长度相等的木棒(长度必须是整数)。
#include<cmath>
#include<iostream>
#define eps 1e-5
#define PI acos(-1.0)
//求圆心角之和
int edges[];
double f(double r,int n)
{
double sum = 0;
for (int i = 0; i < n; i++)
{
double alpha = 2*asin(edges[i] / 2 / r);
sum += alpha;
}
return sum;
}
int main()
{
int N;//边数
scanf("%d", &N);//输入边数
double sum;//圆心角之和
double maxAngle = 0.0;//最长边对应的圆心角
double maxEdge = 0.0;//最长边
//初始化edges
for (int i = 0; i < N; i++)
{
scanf("%lf", &edges[i]);
if (edges[i] > maxEdge)
maxEdge = edges[i];//保存最大边
}
//取最长边作为直径
sum = f(maxEdge / 2, N);
if (abs(sum - 2 * PI) < eps)
{
printf("外接圆的最大半径是最大边的一半:%.2f", maxEdge / 2);
return 0;
}
//半径大于最大边的一半(即斜边大于直角边)
double left = maxEdge / 2, right = 10000, mid,other;
while (right - left > eps)
{
mid = left + (right - left) / 2;
maxAngle = asin(maxEdge / 2 / mid) * 2;//求出最大边对应的圆心角
sum = f(mid, N);
other = sum - maxAngle;
//如果除去最大圆心角的其他圆心角之和小于π,说明圆心在多边形外面
if (other < PI)
{
sum = other + 2 * PI - maxAngle;
if (sum < 2 * PI)
left = mid;
else
right = mid;
}
//圆心在多边形里面
else
{
if (sum > 2 * PI)
left = mid;
else
right = mid;
}
printf("外接圆的最大半径是:%.2f", mid);
return 0;
}
}
参考:https://blog.csdn.net/flashmsn/article/details/94642687
https://www.cnblogs.com/transmigration-zhou/p/12275464.html