当前位置:   article > 正文

二分查找:万能模板及详细例题_二分查找案例

二分查找案例

本篇为学习笔记,学习视频为:二分查找 | 妈妈再也不怕我写错二分啦 | 五点七边二分视频补充_哔哩哔哩_bilibili

目录

        整数二分

万能模板:

做题步骤:

例题

二分查找例题1: 数的范围

二分查找例题2:A - B数对

二分答案例题3:P1873-砍树

二分答案例题4:P2440-木材加工

二分答案例题5:P2678-跳石头

浮点二分 - 求三次方根


        整数二分

万能模板:

  1. int l = -1,r = N;
  2. while(l + 1 != r){
  3. int mid = l + r >> 1;
  4. if(isBlue(mid)) l = mid;
  5. else r = mid;
  6. }

isBlue是一个判断函数,最后停止时,l 指蓝色的最后一个数,r指红色的第一个数,两者相邻且有一个看不见的分界线,如下图:

做题步骤:

  • 写出 isBlue 函数
  • 判断最后返回应该是l还是r
  • 套模板

例题

二分查找例题1: 数的范围

给定一个按照升序排列的长度为 n 的整数数组,以及 q 个查询。

对于每个查询,返回一个元素 k 的起始位置和终止位置(位置从 0 开始计数)。

如果数组中不存在该元素,则返回 -1 -1

要找到x的起始位置和结束位置,也就是要找到第一个不<x和最后一个不> x

  • 起始位置:isBlue : < x, return r  
  • 结束位置:isBlue :<= x,return l  
  1. #include<iostream>
  2. using namespace std;
  3. const int N = 100010;
  4. int nums[N];
  5. int n,q;
  6. int main(){
  7. cin >> n >> q;
  8. for(int i = 0;i < n;i++) cin >> nums[i];
  9. for(int i = 0;i < q;i++){
  10. int x;
  11. cin >> x;
  12. int l = -1,r = n;
  13. while(l + 1 != r){
  14. int mid =( l + r )>> 1;
  15. if(nums[mid] < x) l = mid;
  16. else r = mid;
  17. }
  18. if(nums[r] != x) cout << "-1 -1" << endl;
  19. else{
  20. cout << r << " ";
  21. l = -1,r = n;
  22. while(l + 1 != r){
  23. int mid =( l + r )>> 1;
  24. if(nums[mid] <= x) l = mid;
  25. else r = mid;
  26. }
  27. cout << l << endl;
  28. }
  29. }
  30. return 0;
  31. }

二分查找例题2:A - B数对

https://www.luogu.com.cn/problem/P1102

给出一串正整数数列以及一个正整数 C,要求计算出所有满足 A−B=C 的数对的个数(不同位置的数字一样的数对算不同的数对)。

 思路:转换为A = B + C,枚举B,然后在数组中找A是否存在 

注意:int: 2的30次方  long long:2的60次方,因此count需要开long long

  1. #include<iostream>
  2. #include<algorithm>
  3. using namespace std;
  4. int n,c;
  5. const int N = 200010;
  6. int nums[N];
  7. int bsearch1(int x){
  8. int l = -1,r = n;
  9. while(l + 1 != r){
  10. int mid = (l + r) >> 1;
  11. if(nums[mid] < x) l = mid;
  12. else r = mid;
  13. }
  14. if(nums[r] == x) return r;
  15. else return -1;
  16. }
  17. int bsearch2(int x){
  18. int l = -1,r = n;
  19. while(l + 1 != r){
  20. int mid = ( l + r) >> 1;
  21. if(nums[mid] <= x) l = mid;
  22. else r = mid;
  23. }
  24. if(nums[l] == x ) return l;
  25. else return -1;
  26. }
  27. int main(){
  28. cin >> n >> c;
  29. long long count = 0;
  30. for(int i = 0;i < n;i++) cin >> nums[i];
  31. sort(nums,nums + n);
  32. for(int b = 0;b < n;b++){
  33. int b1 = bsearch1(nums[b] + c);
  34. if(b1 != -1){
  35. int b2 = bsearch2(nums[b] + c);
  36. count += b2 - b1 + 1;
  37. }
  38. }
  39. cout << count;
  40. return 0;
  41. }

二分答案例题3:P1873-砍树

P1873 [COCI 2011/2012 #5] EKO / 砍树 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

二分答案是将所有结果集分为满足和不满足两部分,即求最小答案的最大值 or 最大答案的最小值时考虑

此题分类时一定注意是 要满足部分的最小值,也就是res>=m返回l ,而不要想着要求m

  1. #include<iostream>
  2. #include<algorithm>
  3. #include<cmath>
  4. using namespace std;
  5. int n, m;
  6. const int N = 1000010;
  7. int nums[N];
  8. bool check(int x){
  9. int res = 0;
  10. for (int i = 0; i < n; i++) {
  11. if (nums[i] - x > 0) {
  12. res += nums[i] - x;
  13. }
  14. if(res >= m) return true;
  15. }
  16. return false;
  17. }
  18. //至少得到M米
  19. int main() {
  20. cin >> n >> m;
  21. int maxh = 0;
  22. for (int i = 0; i < n; i++) {
  23. cin >> nums[i];
  24. maxh = max(nums[i], maxh);
  25. }
  26. int l = 0,r = maxh + 1;
  27. while(l + 1 != r){
  28. int mid = (l + r) >> 1;
  29. if(check(mid)) l = mid;
  30. else r = mid;
  31. }
  32. if(check(r)) cout << r << endl;
  33. else cout << l << endl;
  34. return 0;
  35. }

二分答案例题4:P2440-木材加工

P2440 木材加工 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

将木头按长度l切,使所有的木头切完后可以正好有k段,l越大越好,因此本题应该从l出发,枚举每种l的可能,找到恰好是不满足题意的那个点

  1. #include<iostream>
  2. #include<algorithm>
  3. #include<cmath>
  4. using namespace std;
  5. int n, k;
  6. const int N = 100010;
  7. int nums[N];
  8. bool check(int x){
  9. int ans = 0;
  10. for(int i = 0;i < n;i++){
  11. ans += nums[i] / x;
  12. if(ans >= k) return true;
  13. }
  14. return false;
  15. }
  16. int main() {
  17. cin >> n >> k;
  18. int longest = 0;
  19. for(int i = 0;i < n;i++){
  20. cin >> nums[i];
  21. longest = max(longest,nums[i]);
  22. }
  23. int l = 0,r = longest + 1;
  24. while(l + 1 != r){
  25. int mid = (l + r) >> 1;
  26. if(check(mid)) l = mid;
  27. else r = mid;
  28. }
  29. if(check(r)) cout << r << endl;
  30. else cout << l << endl;
  31. return 0;
  32. }

二分答案例题5:最小值最大-P2678-跳石头

P2678 [NOIP2015 提高组] 跳石头 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

本题我刚开始的思路是,记录最短距离然后每次找到最小的那个删除,但是这样无法保证最后是最优解,

 方法1:枚举搬走哪块石头,但选择太多,一定会超时

方法2:枚举答案,即枚举最小跳跃距离

  1. #include<iostream>
  2. #include<algorithm>
  3. #include<vector>
  4. using namespace std;
  5. int L, n, m;
  6. const int N = 50010;
  7. int nums[N];
  8. //x为此时最小跳跃距离
  9. bool check(int x) {
  10. int i = 0,j = 0;
  11. int cnt = 0;
  12. while(i < n + 1) {
  13. i++;
  14. if (nums[i] - nums[j] < x) {
  15. cnt++;
  16. }
  17. else {
  18. j = i;
  19. }
  20. }
  21. if (cnt > m) return false;
  22. else return true;
  23. }
  24. int main() {
  25. cin >> L >> n >> m;
  26. for (int i = 1; i <= n; i++) {
  27. cin >> nums[i];
  28. }
  29. nums[0] = 0;
  30. nums[n + 1] = L;
  31. int l = 0, r = L + 1;
  32. while (l + 1 != r) {
  33. int mid = (l + r) >> 1;
  34. if (check(mid)) l = mid;
  35. else r = mid;
  36. }
  37. if (check(l)) cout << l;
  38. else cout << r;
  39. return 0;
  40. }

二分答案例题6:最大值最小-P路标设置

P3853 [TJOI2007] 路标设置 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

  1. #include<iostream>
  2. using namespace std;
  3. int L, n, k;
  4. const int N = 100010;
  5. int nums[N];
  6. int s[N];
  7. bool check(int x) {
  8. int cnt = 0;
  9. for (int i = 1; i <= n; i++) {
  10. if (s[i] > x) {
  11. cnt++;
  12. int num = s[i] - x;
  13. while (num > x) {
  14. cnt++;
  15. num -= x;
  16. }
  17. }
  18. }
  19. if (cnt > k) return true;
  20. else return false;
  21. }
  22. //枚举路标的最大距离的最小值
  23. int main() {
  24. cin >> L >> n >> k;
  25. for (int i = 1; i <= n; i++) {
  26. cin >> nums[i];
  27. s[i] = nums[i] - nums[i - 1];
  28. }
  29. int l = 0, r = L + 1;
  30. while (l + 1 != r) {
  31. int mid = (l + r) >> 1;
  32. if (check(mid)) l = mid;
  33. else r = mid;
  34. }
  35. if (!check(r)) cout << r;
  36. else cout << l;
  37. return 0;
  38. }

浮点二分 - 求三次方根

给定一个浮点数 n,求它的三次方根。

  1. #include<iostream>
  2. using namespace std;
  3. int main(){
  4. double x;
  5. cin >> x;
  6. double l = -100,r = 100;
  7. while(r - l > 1e-8){
  8. double mid = (l + r) /2;
  9. if(mid * mid * mid <= x) l = mid;
  10. else r = mid;
  11. }
  12. printf("%.6f",l);
  13. return 0;
  14. }

优化一下,可以直接二分一百次来求结果:

  1. #include<iostream>
  2. using namespace std;
  3. int main(){
  4. double x;
  5. cin >> x;
  6. double l = -100,r = 100;
  7. for(int i = 0;i < 100;i++){
  8. double mid = (l + r) /2;
  9. if(mid * mid * mid <= x) l = mid;
  10. else r = mid;
  11. }
  12. printf("%.6f",l);
  13. return 0;
  14. }

以上是本文全部内容,如果对你有帮助点个赞再走吧~  ₍˄·͈༝·͈˄*₎◞ ̑̑

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/386048
推荐阅读
相关标签
  

闽ICP备14008679号