备赛蓝桥杯之二分

梁老师
梁老师 北京小升初老师~

0 人点赞了该文章 · 60 浏览




今天复习的是二分。二分分为整数二分、实数二分。知识点就不再讲解了。忘记的话看看这篇文章二分。下面就直接讲解两道二分题!
整数二分
题目:查找最接近的元素
题目描述
在一个非降序列中,查找与给定值最接近的元素。
输入格式
第一行包含一个整数n,为非降序列长度。1 <= n <= 100000。
第二行包含n个整数,为非降序列各元素。所有元素的大小均在0-1,000,000,000之间。
第三行包含一个整数m,为要询问的给定值个数。1 <= m <= 10000。
接下来m行,每行一个整数,为要询问最接近元素的给定值。所有给定值的大小均在0-1,000,000,000之间。
输出格式
m行,每行一个整数,为最接近相应给定值的元素值,保持输入顺序。若有多个值满足条件,输出最小的一个。
样例输入
32 5 82105
样例输出
85
题目链接
https://www.dotcpp.com/oj/problem2926.html
思路

我第一眼看看就是简单的一个二分,然后就迅速写了一下就提交,结果才对了9分。

图片

为什么错呢?我也百思不得其解。看看别人的题解不知道,为什么最后还要判断一下。后来又在网上搜了一下这个题,发现了一个重要的地方。那就是我读题不认真,需要求的是最接近的元素,自然需要进行判断。所以说,下次做题之前要认真读题!!!

来说说思路吧!

首先,咱们为了减少运行时间。把要查找的元素 x 进行一个划分,分为两大类,三小类。两大类是元素 x 是否在序列中。如果不在序列中,直接输出序列中最大/最小的元素即可;在序列中的时候再进行二分即可!如下分为三种情况。

  1. 要查找的元素 x 小于序列中的首元素。此时,首元素最接近 x ,输出首元素即可

  2. 要查找的元素 x 大于序列中的最后一个元素此时。最后一个元素最接近 x,输出最后一个元素即可

  3. 查找的元素在序列里面,再进行二分操作。



前两种情况很简单,就不说了,咱们看看第三种情况。
当咱们最后二分出来一个数 num(下标为 l ) 的时候,此时 num 一定存在两个相邻的数,q[ l - 1] ≤ num ≤ q[l + 1]。这时候咱们应该进行判断,看看这两个数谁最接近 x 。
  • 当 | q[ l - 1] - x | < | q[ l ] - x | 时,表示 q[l - 1] 最接近 x ,输出 q[l-1]即可。

  • 当  q[ l - 1] - x | = | q[ l ] - x 时,由于题目要求多个值满足时,输出最小值即可。所以输出 q[ l - 1 ] 即可。


  •  q[ l - 1] - x | > | q[ l ] - x | 时,表示 q[ l ] 最接近 x ,输出 q[ l ] 即可。



综上所述,当 | q[ l - 1 ] - x | <= | q[ l ]  - x | 时,q[l - 1]最接近 x ;否则 q[ l ]接近。

而二分出来 num 的方法有两种,第一种是 二分出 第一个 大于等于 x 的值,也就是 大于等于 x 的最小值;第二种是二分出 最后一个 小于等于 x 的值,也就是 小于等于 x 的最大值。

第一种方法的模板

int l = 0,r = n - 1;while(l < r){    int mid = (l + r) / 2;    if(q[mid] >= x)        r = mid;    else        l = mid + 1;}

第二种方法的模板

int l = 0,r = n - 1;while(l < r){    int mid = (l + r + 1) / 2;    if(q[mid] <= x)        l = mid;    else        r = mid - 1;}
题解
第一种二分方法代码
#include<iostream>#include <cmath>
using namespace std;
const int N = 1e5 + 10;int q[N],n,m;
int main(){ scanf("%d",&n); for(int i = 0;i < n;i++) scanf("%d",&q[i]); scanf("%d",&m); int x; while(m--) { scanf("%d",&x); if(x <= q[0]) printf("%d\n",q[0]); else if(x >= q[n - 1]) printf("%d\n",q[n - 1]); else { int l = 0,r = n - 1; while(l < r) { int mid = (l + r) / 2; if(q[mid] >= x) r = mid; else l = mid + 1; } if(abs(q[l] - x) < abs(q[l - 1] - x)) printf("%d\n",q[l]); else printf("%d\n",q[ l - 1]); } } return 0;}
第二种二分方法代码
#include<iostream>#include <cmath>
using namespace std;
const int N = 1e5 + 10;int q[N],n,m;
int main(){ scanf("%d",&n); for(int i = 0;i < n;i++) scanf("%d",&q[i]); scanf("%d",&m); int x; while(m--) { scanf("%d",&x); if(x <= q[0]) printf("%d\n",q[0]); else if(x >= q[n - 1]) printf("%d\n",q[n - 1]); else { int l = 0,r = n - 1; while(l < r) { int mid = (l + r + 1) / 2; if(q[mid] <= x) l = mid; else r = mid - 1; } if(abs(q[l] - x) <= abs(q[l + 1] - x)) printf("%d\n",q[l]); else printf("%d\n",q[ l + 1]); } } return 0;}
参考博客
https://blog.csdn.net/lq1990717/article/details/124441505
实数二分
题目:二分法求函数零点
题目描述
有函数:
f(x) = x5 - 15 * x4+ 85 * x3- 225 * x2+ 274 * x - 121
已知 f(1.5) > 0 , f(2.4) < 0 且方程 f(x) = 0 在区间 [1.5,2.4] 有且只有一个根,请用二分法求出该根。
输入格式

输出格式
该方程在区间[1.5,2.4]中的根。要求四舍五入到小数点后6位。
样例输入
样例输出
思路

其实也没有什么难的,有两个需要注意的点。

第一个是控制小数点的位数,while循环的条件是 r - l >1e-8。至于为什么请看之前的文章实数二分

第二个是二分的条件。什么情况需要二分哪一个区间。对于这个求函数的零点,咱们需要知道一个定理就是零点存在定理。

https://baike.baidu.com/item/%E5%8B%98%E6%A0%B9%E5%AE%9A%E7%90%86?fromtitle=%E9%9B%B6%E7%82%B9%E5%AD%98%E5%9C%A8%E5%AE%9A%E7%90%86&fromid=23707175&fromModule=lemma_search-box

内容:函数在一个区间(a,b)上连续,且 f(a) * f(b) < 0,表明这个区间一定存在一个零点!

题解

#include <iostream>#include <cmath>
using namespace std;
double function(double x){ return pow(x,5) - 15 * pow(x,4) + 85 * pow(x,3 ) - 225 * pow(x,2) + 274 * x - 121; }
int main(){ double l = 1.5,r = 2.4; while(r - l > 1e-8) { double mid = (l + r) / 2;        if(function(l) * function(mid) < 0)// 表明[l,mid]存在零点 r = mid;// 更新区间方式 else// 否则[mid,r]里面存在零点 l = mid; } printf("%lf\n",l); return 0;}

图片


添加 家长论坛微信 



发布于 2024-04-27 12:00

免责声明:

本文由 梁老师 原创发布于 家长帮 ,著作权归作者所有。

登录一下,更多精彩内容等你发现,贡献精彩回答,参与评论互动

登录! 还没有账号?去注册

暂无评论

广告
All Rights Reserved Powered BY WeCenter V4.1.0 © 2025 京ICP备20005761号-2