当前位置: 首页 > article >正文

算法13、基础二分查找的应用(木根切割等)

🌰1、方程求根

晴问算法

  1️⃣即求f(x) = x^3 + x^2 + x - a = 0的根,又因为要求精确到0.01,所以eps至少设置为1e-3或者更小;

  2️⃣求导得3x^2 + 2x + 1 = 2x^2 + x^2 + 2x + 1 = 2x^2 + (x+1)^2  > 0, 所以f(x)是单调递增函数;

  3️⃣估计查找区间初值:因为a的取值区间是-100-100,所以我们让查找区间的初值为【-100 , 100】

这样一定能包含所有可能的解。

  对于闭区间【left, right]:

        若f(mid) > 0,说明mid比解大,则让right = mid;

        若 < 0, 说明mid比解小,则让left = mid;

  当right - left <= eps 则可跳出循环。返回left or mid, 即进入循环条件是right-left > eps,最终取两位小数输出

#include <iostream>
using namespace std;
const double EPS = 1e-3;//
int a;
double f(double x){
    return x*x*x + x*x + x -a ;
}
double binarySearch() {
    double left = -100, right = 100, mid;
    while (right - left > EPS) {
         mid = (left + right) / 2;
        if (f(mid) > 0) {
            right = mid;
        } else {
            left = mid;
        }
    }
    return left;//or mid,但是运行时间会更长。
}

int main() {
    cin >> a;
    printf("%.2f", binarySearch());
    return 0;
}

 🌰2、装水问题

晴问算法

1️⃣我们要求h,所以应当对h进行二分查找,又要求精确到2位小数,所以eps至少是1e-3;

2️⃣又显然随着h的增大,s1的面积也增大,所以当我们对h进行二分查找时,

        若s1/s2 > r说明s1过大了,应该缩小右边界,即让right = mid;

        若s1/s2 < r说明s1过小了,应该扩大左边界,即让left = mid;

当right - left <= eps,就找到了满足要求的解,跳出循环。返回左边界。输出带前2位小数的格式。

3️⃣二分查找区间的初值应该是[0, R], 因为它覆盖了h所有可能的取值。

为了统一代码,我们定义函数f(R, h) = s1/s2

#include <cstdio>
#include <cmath>
const double EPS = 1e-3;
const double PI = acos(-1.0);
double f(double R, double 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 binarySearch(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 left;
}
int main(){
    int R;
    double r;
    scanf("%d%lf", &R, &r);
    printf("%.2lf\n", binarySearch(R, r));
}

🌰3、木棒切割问题

晴问算法

做法1-转化为基础二分查找

对切割后的长度l进行二分查找,设k为长度l下可得到的最大段数。

  转化思路:
        要求得到的段数至少K段,那么求最大长度也就是求:最后一个满足k>=K的length,再转化一下,就是求:第一个满足k<K的length, 再减1。这样就可以运用基础二分查找的思路找到第一个满足k<K的length了:

对于二分查找区间[left, right]:

        若k < K,说明mid有可能是解,但还需往左寻找,因为要求第一个满足k<K的length,那就让right = mid;

        若k >= K, 说明k过大,也就是length过小,那么就让left = mid + 1;

当left == right则找到了解,所以进入循环的条件是left < right.

  ⚠️除数不能为0:

  二分查找区间的初值本应该是[0, max(a[i]) ],即若返回值是0(0-1==-1)或者1(1-1==0),都说明无法达成,都应该输出0,所以应该额外判断返回值为0时,直接输出0不要再-1了。

  但是,由于计算当前mid可得到的最大段数用到了除法,除数length不能为0,

  因此,查找区间的初值应该从1开始,保证除数不会为0,即left应初始化为1.这样,最后就能够统一输出返回值-1,不需要额外判断返回值了。

返回left or right,最后将返回值-1得到最终结果。

#include <iostream>
using namespace std;
const int MAXN = 1005;
int a[MAXN];//存木棒长度
int n, K;
int Num(int length){
    int ans = 0;
    for(int i = 0; i < n; i++)
        ans += a[i] / length;//注意除法要求除数不为0!!
    return ans;
}
int binarySearch(int right){
    int left = 1, mid;
    while(left < right){
        mid =  (left + right ) / 2;
        if(Num(mid) < K)
            right = mid;
        else 
            left = mid + 1;
    }
    return left;// or right;
}
int main(){
    cin >> n >> K;
    int maxL = 0;
    for(int i = 0; i < n; i++){
        cin >> a[i];
        if(a[i] > maxL)
            maxL = a[i];
    }
    printf("%d\n", binarySearch(maxL)-1 );
}
做法2

  1️⃣题目要求切割后每根木棒的最大长度,那么就应该对切割后每根木棒的长度进行二分查找,那么查找区间初值为[0,max(木棒长度)]

  2️⃣又显然,要求切割后得到的木棒段数越多即k越大,长度就会越小,设函数Num(l)为长度为l时可得到的最多段数

对于二分查找区间[left, right]:

        若Num(mid) < k, 不满足至少k段的条件,说明mid不可能是解,且应该让k大一些,那就应该让mid小一点,即让right = mid -1;

        若Num(mid) >= k,满足至少k段的条件,说明mid有可能是解,但还需让mid大一点继续往右检查看是否还有更长的mid满足至少k段,那就让left = mid;

当left == right说明找到了最大长度即解,因此进入循环的条件应该是left < right;

最终应该返回left或者right;

Num(int length) = a1 / length + a2 / length + ……

注意,计算mid时,为了防止left=0,right=1时, 此时mid==0进入Num函数导致的除数为0,应该在此加上1再除以2,

        而且,若没加上1时,当right = left + 1时,设此时left = x, 那么mid = (left + right) /2会等于left==x, 若此时num(mid) >= k那么left = mid == x, 进入下一个循环又是mid=left==x, mid,left就会一直相等num(mid)结果一直不变导致死循环。

#include <iostream>
using namespace std;
const int MAXN = 1005;
int a[MAXN];//存木棒长度
int n, k;
int Num(int length){
    int ans = 0;
    for(int i = 0; i < n; i++)
        ans += a[i] / length;//注意除法要求除数不为0!!
    return ans;
}
int binarySearch(int right){
    int left = 0, mid;
    while(left < right){
        mid =  (left + right + 1) / 2;
        if(Num(mid) < k)
            right = mid - 1;
        else 
            left = mid;
    }
    return left;// or right;
}
int main(){
    cin >> n >> k;
    int maxL = 0;
    for(int i = 0; i < n; i++){
        cin >> a[i];
        if(a[i] > maxL)
            maxL = a[i];
    }

    printf("%d\n", binarySearch(maxL));
}

http://www.kler.cn/a/468538.html

相关文章:

  • 【论文+源码】基于Spring和Spring MVC的汉服文化宣传网站
  • 论文解读 | NeurIPS'24 IRCAN:通过识别和重新加权上下文感知神经元来减轻大语言模型生成中的知识冲突...
  • 牛客网刷题 ——C语言初阶(6指针)——字符逆序
  • 【软考网工笔记】计算机基础理论与安全——网络规划与设计
  • windows中硬件加速gpu计划开启cpu的使用率居高不下
  • 模型参数公式与代码对应
  • kubernetes-循序渐进了解coredns
  • 打造三甲医院人工智能矩阵新引擎(二):医学影像大模型篇--“火眼金睛”TransUNet
  • Spring Boot教程之四十九:Spring Boot – MongoRepository 示例
  • 【数据结构与算法:二、线性表】
  • Zookeeper模式安装Kafka(含常规、容器两种安装方式)
  • SpringBoot的6种API请求参数读取方式
  • 【C++】P1428 小鱼比可爱
  • Unity开发2d游戏全套教程[入门案例]
  • 0-基于蚁群优化和带注意力机制的循环神经网络的新型混合算法用于解决旅行商问题(HAL science)(完)
  • 【数据结构与算法:五、树和二叉树】
  • Springboot使用Rabbitmq的延时队列+死信队列实现消息延期消费
  • 快速将索尼手机联系人导出为 HTML 文件
  • 2024 年度时序数据库 IoTDB 论文总结
  • From matplotl1b.path 1mport failed to import ImportError:numpy.core.multiarray
  • CentOS — 群组管理
  • NVIDIA DLI课程《NVIDIA NIM入门》——学习笔记
  • 【USRP】教程:在Macos M1(Apple芯片)上安装UHD驱动(最正确的安装方法)
  • 【C++】矩阵转置问题详解与优化
  • 机器学习导论笔记
  • Hadoop•配置网络克隆虚拟机