首先我们先来从一个小游戏理解一下二分。(摘自程序员小灰的博客

为什么说这样效率最高呢?因为每一次选择数字,无论偏大还是偏小,都可以让剩下的选择范围缩小一半。

给定范围0到1000的整数:

第一次我们选择500,发现偏大了,那么下一次的选择范围,就变成了1到499:



第二次我们选择250,发现还是偏大了,那么下一次的选择范围,就变成了1到249:

第三次我们选择125,发现偏小了,那么下一次的选择范围,就变成了126到249:

以此类推,最坏的情况需要猜测多少次呢?答案是 log1000 = 10次,也就是让原本的区间范围进行10次 “折半”。

所以我们很容易发现,二分查找并不会占用更多的空间,但是可以让时间复杂度从O(n)缩减到O(log(n)),这样的速度优化在程序中是非常有效的。

其次就是代码实现问题。

基本代码先贴上,先看看能不能理解:

#include<bits/stdc++.h>
using namespace std;
int a[100+1];
int main()
{
    int x,n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    cin>>x;
    int left=1,right=n;
    while(left<=right)
    {
        int mid=(left+right)/2;
        if(x==mid){cout<<"Find."<<endl<<mid;break;
        }
          if(mid>x)
          {
            right=mid+1;
          }
          else left=mid-1;
    }
    if(left>right)cout<<"Not find."<<endl;
    return 0;
}

left即本次查找区间的左边界,right同理,mid即中点,是下次折半的参照点。

如果要查找的数比中点(mid)还要小,下一次的查找区间就是从left~mid-1,

如果要查找得数比中点(mid)还要大,下一次的查找区间就是从mid+1~right.

如果左边界(left)比右边界(right)还要大,说明这个区间里没有要查找的数,即无法找到。

如果要查找的数就等于中点(mid),说明我们找到了要查找的数。

这几步如果不能理解请参照上面的漫画。

当然,递归也是可以实现的:

void binsearch(int left,int right)
{
    if(left<=right)
    {
        int mid=(left+right)/2;
        if (a[mid]==x) {cout<<"Find"<<endl;return;}
        if (x<a[mid]) binsearch(left,mid-1);
        else binsearch(mid+1,right);
    }
    else cout<<"Not Find"<<endl;
}

大致掌握了概念之后来看看例题;

火车站台连锁店
描述

蒜头君建立了一家火车站台连锁店,要在一条铁路线的所有车站里,选择一部分车站开办连锁店,销售各种口味的大蒜。

铁路线上有 n 个车站,假设这条铁路线是一条直线,其中每个站点的坐标为 x1,x2,x3,x4...xn

蒜头君一共要开办 m 个连锁店,并且不希望连锁店离得太近,以使得整体的收益最大化。他希望他的连锁店之间的最近距离尽可能大,你能帮他算出这个最大的最近距离吗?

输入
第一行输入用空格分隔的两个整数 n,m(2≤n≤10^5,2≤m≤n),分别表示车站数量和连锁店数量。
接下来一共 n 行,每行一个整数 xi,0≤xi≤10^9,表示车站的坐标。

输出
输出一行整数,表示最大的最近距离。

输入样例 1 

6 3
1
3
5
2
7
9
输出样例 1

4
输入样例 2 

5 4
5
7
10
28
9
输出样例 2

2

先带着大家理解下题目;

在一段有n个可以开设站台的位置上,请选取其中的m个,使得每两个站台之间的最小距离取得最大值。

大概就是说,你要让这m个站台中,每两个站台之间的距离都尽可能的大。

这道题的二分思路有点复杂emm

首先我们不能直接二分因为压根找不到可以二分的东西

然后因为题目要求输出最短位置,我索性就把最短距离拿来二分!

但是我们需要一个辅助函数count来计算以L为最短距离能够开设多少家店。

函数构造很简单,直接贴上来了:

int count(int l)
{
    int last=1,ans=1;
    for(int i=1;i<=n;i++)
    {
        while(a[i]-a[last]<l)
        {
            i++;
            if(i>n)return ans;
        }
        last=i;
        ans++;
    }
    return ans;
}

怎么实现查找?

1.首先确定初始范围

因为我们要把最短距离进行二分查找,所以我们需要一个最初始的left以及right的值。可以肯定的是,这个最短距离L一定在1~最大坐标-最小坐标。

2.画图模拟查找过程!

先假设这个图长这个样子:(n=5,m=4)



第一次L的查找的区间是1~13此时mid=7,将mid传入函数count,发现可以开设2家店。

因为2<m(4),所以下一次查找区间变成1~7。(left=1,right=7)

第二次L的查找的区间是1~7此时mid=4,将mid传入函数count,发现可以开设3家店。

因为3<m(4),所以下一次查找区间变成1~4.(left=1,right=4)

第三次L的查找区间是1~4此时mid=2,将mid传入函数count,发现可以开设5家店。

因为5≥m(4),所以下一次查找区间变成2~4.(left=2,right=4)

第四次L的查找区间是2~4,此时mid=3,将mid传入函数count,发现可以开设4家店。

因为4≥m(4),所以下一次查找区间变成3~4(left=3,right=4)

接下来如果不停止,left和right和mid的值再也不会发生任何改变!

为什么?

我们二分的写法是while(left<right)

等等!发现什么不对了吗?

因为我们二分的写法是每次left和right其一等于mid,但当它们其中一个和mid相等的时候,这就成了一个死循环!

所以我们改成:

while(right-left>1)

这样就很好的避免了这个问题!

但是,走出二分之后,我们该输出谁的值?right?

这样是不完全正确的!

可能在最短距离为right的时候,我们无法开设得了m个店!因为我们while的写法,所以我们必须多一下判断:

if(count(right)>=m)cout<<right<<endl;
    else cout<<left<<endl;

所以当n=5,m=4,图如下的时候,应该输出的数字是3;

完整代码如下:

#include<bits/stdc++.h>
using namespace std;
int a[100000+1];
int n,m;
int count(int l)
{
    int last=1,ans=1;
    for(int i=1;i<=n;i++)
    {
        while(a[i]-a[last]<l)
        {
            i++;
            if(i>n)return ans;
        }
        last=i;
        ans++;
    }
    return ans;
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    sort(a+1,a+1+n);
    int left=1,right=a[n]-a[1];
    while(right-left>1)
    {
        int mid=(left+right)/2;
        if(count(mid)>=m)
        {
            left=mid;
        }
        else right=mid;
    }
    if(count(right)>=m)cout<<right<<endl;
    else cout<<left<<endl;
    return 0;
}

ov.

【二分讲解及例题】火车站台连锁店-C++的更多相关文章

  1. 【dfs基础讲解及例题】

    定义 DFS(Depth-First-Search)深度优先搜索算法,是搜索算法的一种. 接下来因为懒得去找大段大段深奥的材料 所以就是一些个人的理解. 所谓深搜,是相对于广搜(只是第一篇)来说的.深 ...

  2. 【高斯消元】兼 【期望dp】例题

    [总览] 高斯消元基本思想是将方程式的系数和常数化为矩阵,通过将矩阵通过行变换成为阶梯状(三角形),然后从小往上逐一求解. 如:$3X_1 + 2X_2 + 1X_3 = 3$ $           ...

  3. 51nod 1105 二分答案法标准题目

    二分答案法例题,用于练习二分答案的基本思想非常合适,包括了思维方式转换的内容(以前我们所做的一直是利用二分法求得数组元素对应指针之类,但是现在是直接对答案进行枚举). 思路是:首先对输入数组进行排序, ...

  4. luogu P1316 丢瓶盖 |二分答案

    题目描述 陶陶是个贪玩的孩子,他在地上丢了A个瓶盖,为了简化问题,我们可以当作这A个瓶盖丢在一条直线上,现在他想从这些瓶盖里找出B个,使得距离最近的2个距离最大,他想知道,最大可以到多少呢? 输入格式 ...

  5. 数据结构和算法:Python实现二分查找(Binary_search)

    在一个列表当中我们可以进行线性查找也可以进行二分查找,即通过不同的方法找到我们想要的数字,线性查找即按照数字从列表里一个一个从左向右查找,找到之后程序停下.而二分查找的效率往往会比线性查找更高. 一. ...

  6. P1527 [国家集训队]矩阵乘法(整体二分)

    Link 整体二分的经典例题. 对于整体二分,我个人的理解是二分答案套分治. 具体来说就是对答案进行二分,然后对于询问进行类似于权值线段树求区间第 \(k\) 大的分治做法. 首先,我们暴力做法就是对 ...

  7. 差分数组 and 树上差分

    差分数组 定义 百度百科中的差分定义 //其实这完全和要讲的没关系 qwq 进去看了之后是不是觉得看不懂? 那我简单概括一下qwq 差分数组de定义:记录当前位置的数与上一位置的数的差值. 栗子 容易 ...

  8. C语言-sizeof()与strlen()的区别【转】

    先看看sizeof() 一.sizeof的概念 sizeof是C语言的一种单目操作符,如C语言的其他操作符++.--等.它并不是函数.sizeof操作符以字节形式给出了其操作数的存储大小.操作数可以是 ...

  9. 有关 sizeof和strlen的总结

    先看看sizeof() 一.sizeof的概念 sizeof是C语言的一种单目操作符,如C语言的其他操作符++.--等.它并不是函数.sizeof操作符以字节形式给出了其操作数的存储大小.操作数可以是 ...

随机推荐

  1. Qt之自定义搜索框——QLineEdit里增加一个Layout,还不影响正常输入文字(好像是一种比较通吃的方法)

    简述 关于搜索框,大家都经常接触.例如:浏览器搜索.Windows资源管理器搜索等. 当然,这些对于Qt实现来说毫无压力,只要思路清晰,分分钟搞定. 方案一:调用QLineEdit现有接口 void ...

  2. 内存可用性判断 IsBadCodePtr IsBadReadPtr 等等

    程序异常崩溃,多数是有内存访问异常引起.为定位崩溃位置通常考虑加强内存访问控制,如此有必要进行内存可用性判断,从<Windows核心编程>中看到内存指针的可用性判断方法,感觉还不错,此处记 ...

  3. WPF 调用API修改窗体风格实现真正的无边框窗体

    原文:WPF 调用API修改窗体风格实现真正的无边框窗体 WPF中设置无边框窗体似乎是要将WindowStyle设置为None,AllowTransparency=true,这样才能达到WinForm ...

  4. Advanced Installer 安装前卸载旧版本的办法

    原文:Advanced Installer 安装前卸载旧版本的办法 Advanced Installer这个工具百度出来的资料太少了. 在我们平常打包的工作中,经常遇到的一个问题是,如何能在安装新版本 ...

  5. mysql三种修改密码的方式

    [root@MySQL ~]# mysqladmin -uroot -proot -S /data/3307/mysql.sock password '123'; 其中-p是现在的密码,passwor ...

  6. ORA-13541: system moving window baseline size (691200) greater than retention (432000)

    修改awr生成报告时间间隔和保存时间时报错,由默认的每小时生成,保存8天修改为每半个小时生成一次,保存5天: SQL, retention); , retention); END; * ERROR a ...

  7. 为DataGridTemplateColumn设置快捷菜单

    <DataGrid.ContextMenu> <ContextMenu> <MenuItem Command="{x:Static ApplicationCom ...

  8. EF 里的 join and Group Join

    join ); pageCount = _db.Orders.Count(); return _db.Orders.OrderByDescending(c=>c.ID).Skip(skip).T ...

  9. UWP 发送短信公用倒计时按钮

    1.要求:      发送验证码按钮,点击后,会倒计时60s,之后才能再次点击.不同界面的多个验证码按钮共享这个倒计时时间. 2.操作步骤       1) 从登录界面-->忘记密码输入手机号- ...

  10. sklearn中LinearRegression使用及源码解读

    sklearn中的LinearRegression 函数原型:class sklearn.linear_model.LinearRegression(fit_intercept=True,normal ...