首页 > 其他分享 >二分搜索与二分答案

二分搜索与二分答案

时间:2024-10-08 23:46:55浏览次数:9  
标签:二分 arr le int 样例 mid 搜索 答案 ans

二分前提条件

  • 数组的有序的数组

  • 数组中无重复元素,否则查找的元素下标可以不算唯一的

二分答案

  • 二分答案时需要满足答案的有界性
  • 二分答案的思路:首先判断这个解是否为可行解,然后我们”认为“这个可行解的最优解,然后以这个可行解为标准去左(右)区间寻找最优解

时间复杂度

O(logN)

查找一个数的下标

左闭右闭区间 [l, r]

704. 二分查找 - 力扣(LeetCode)

class Solution {
	public:
		int search(vector<int>& nums, int target) {
			int l = 0, r = nums.size() - 1;
			while (l <= r) {	// 注意是 <=
				int mid = l + (r - l) / 2;	// 防止溢出,等价于 (l + r) / 2
				if (nums[mid] > target)
					r = mid - 1;	// 注意
				else if (nums[mid] < target)
					l = mid + 1;	// 注意
				else return mid;
			}
			return -1;
		}
};

左闭右开区间 [l, r)

class Solution {
	public:
		int search(vector<int>& nums, int target) {
			int l = 0, r = nums.size();	// 注意 r 的初始化
			while (l < r) {
				int mid = l + ((r - l ) >> 1);	// 注意 >> 1 要加括号
				if (nums[mid] > target)
					r = mid;	// 注意
				else if (nums[mid] < target)
					l = mid + 1;	// 注意
				else
					return mid;
			}
			return -1;
		}
};

查找第一次出现的位置

P2249

    int ans = -1;
    int l = 0, r = 7;
    while (l <= r)	// 注意 l == r 时还需要查询
    {
        int mid = l + (r - l >> 1);
        if (arr[mid] > num)
            r = mid - 1;
        else if (arr[mid] < num)
            l = mid + 1;
        else
        {
            ans = mid;
            r = mid - 1;
        }
    }

查找最后一次出现的位置

	int ans = -1;
    int l = 0, r = 7;
    while (l <= r)	// 注意 l == r 时还需要查询
    {
        int mid = l + (r - l >> 1);
        if (arr[mid] > num)
            r = mid - 1;
        else if (arr[mid] < num)
            l = mid + 1;
        else
        {
            ans = mid;
            l = mid + 1;
        }
    }

题目

P1102 A-B数对

题目描述

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

输入格式

输入共两行。

第一行,两个正整数 N,CN,CN,C。

第二行,NNN 个正整数,作为要求处理的那串数。

输出格式

一行,表示该串正整数中包含的满足 AB=CA - B = CA−B=C 的数对的个数。

样例 #1

样例输入 #1

4 1
1 1 2 3

样例输出 #1

3

提示

对于 75%75\%75% 的数据,1N20001 \leq N \leq 20001≤N≤2000。

对于 100%100\%100% 的数据,1N2×1051 \leq N \leq 2 \times 10^51≤N≤2×105,0ai<2300 \leq a_i <2^{30}0≤ai​<230,1C<2301 \leq C < 2^{30}1≤C<230。

2017/4/29 新添数据两组

思路

A = B + C, 二分查找符合条件的A的个数,当然也可以直接用map解决这个问题

const int N = 2e5 + 2;

int arr[N];

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    int n, c;
    cin >> n >> c;
    for (int i = 0; i < n; ++i)
        cin >> arr[i];
    sort(arr, arr + n);
    int ans = 0;
    for (int i = 0; i < n; ++i)
    {
        // 查找第一次和最后一次出现的位置 ans += ind2 - ind 1 + 1
        int num = c + arr[i];
        int ind1 = -1, ind2 = -1;
        int l = 0, r = n - 1;
        while (l <= r)
        {
            int mid = l + (r - l >> 1);
            if (arr[mid] > num)
                r = mid - 1;
            else if (arr[mid] < num)
                l = mid + 1;
            else
            {
                ind1 = mid;
                r = mid - 1;
            }
        }
        l = 0, r = n - 1;
        while (l <= r)
        {
            int mid = l + (r - l >> 1);
            if (arr[mid] > num)
                r = mid - 1;
            else if (arr[mid] < num)
                l = mid + 1;
            else
            {
                ind2 = mid;
                l = mid + 1;
            }
        }
        if (ind1 != -1)
            ans += ind2 - ind1 + 1;
    }
    cout << ans << endl;
    return 0;
}

也可以直接用STL中的二分函数

const int N = 2e5 + 2;

int arr[N];

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    int n, c;
    cin >> n >> c;
    for (int i = 0; i < n; ++i)
    {
        cin >> arr[i];
    }
    sort(arr, arr + n);
    int ans = 0;
    for (int i = 0; i < n; ++i)
    {
        ans += (upper_bound(arr, arr + n, arr[i] + c) - lower_bound(arr, arr + n, arr[i] + c));
    }
    cout << ans << endl;
    return 0;
}

P1873 砍树

题目描述

伐木工人 Mirko 需要砍 MMM 米长的木材。对 Mirko 来说这是很简单的工作,因为他有一个漂亮的新伐木机,可以如野火一般砍伐森林。不过,Mirko 只被允许砍伐一排树。

Mirko 的伐木机工作流程如下:Mirko 设置一个高度参数 HHH(米),伐木机升起一个巨大的锯片到高度 HHH,并锯掉所有树比 HHH 高的部分(当然,树木不高于 HHH 米的部分保持不变)。Mirko 就得到树木被锯下的部分。例如,如果一排树的高度分别为 20,15,1020,15,1020,15,10 和 171717,Mirko 把锯片升到 151515 米的高度,切割后树木剩下的高度将是 15,15,1015,15,1015,15,10 和 151515,而 Mirko 将从第 111 棵树得到 555 米,从第 444 棵树得到 222 米,共得到 777 米木材。

Mirko 非常关注生态保护,所以他不会砍掉过多的木材。这也是他尽可能高地设定伐木机锯片的原因。请帮助 Mirko 找到伐木机锯片的最大的整数高度 HHH,使得他能得到的木材至少为 MMM 米。换句话说,如果再升高 111 米,他将得不到 MMM 米木材。

输入格式

111 行 222 个整数 NNN 和 MMM,NNN 表示树木的数量,MMM 表示需要的木材总长度。

222 行 NNN 个整数表示每棵树的高度。

输出格式

111 个整数,表示锯片的最高高度。

样例 #1

样例输入 #1

4 7
20 15 10 17

样例输出 #1

15

样例 #2

样例输入 #2

5 20
4 42 40 26 46

样例输出 #2

36

提示

对于 100%100\%100% 的测试数据,1N1061\le N\le10^61≤N≤106,1M2×1091\le M\le2\times10^91≤M≤2×109,树的高度 4×105\le 4\times 10^5≤4×105,所有树的高度总和 >M>M>M。

思路

二分答案,即对最大高度4e5进行二分,对每一次二分得到的高度mid进行check,然后缩小区间,直到结束

int n, m;

bool check(int mid, vector<int> arr)
{
    int sum = 0;
    for (auto c : arr)
    {
        if (c > mid)
            sum += c - mid;
        else
            break;
    }
    return sum >= m;
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    cin >> n >> m;
    vector<int> arr(n);
    for (int i = 0; i < n; ++i)
    {
        cin >> arr[i];
    }
    sort(arr.begin(), arr.end(),
         [](int a, int b)
         { return a > b; });
    int l = 0, r = 4e5;
    int ans = 0;
    while (l <= r)
    {
        int mid = l + (r - l >> 1);
        if (check(mid, arr))
        {
            ans = mid;
            l = mid + 1;
        }
        else
            r = mid - 1;
    }
    cout << ans << endl;
    return 0;
}

P2440 木材加工

题目背景

要保护环境

题目描述

木材厂有 nnn 根原木,现在想把这些木头切割成 kkk 段长度lll 的小段木头(木头有可能有剩余)。

当然,我们希望得到的小段木头越长越好,请求出 lll 的最大值。

木头长度的单位是 cm\text{cm}cm,原木的长度都是正整数,我们要求切割得到的小段木头的长度也是正整数。

例如有两根原木长度分别为 111111 和 212121,要求切割成等长的 666 段,很明显能切割出来的小段木头长度最长为 555。

输入格式

第一行是两个正整数 n,kn,kn,k,分别表示原木的数量,需要得到的小段的数量。

接下来 nnn 行,每行一个正整数 LiL_iLi​,表示一根原木的长度。

输出格式

仅一行,即 lll 的最大值。

如果连 1cm\text{1cm}1cm 长的小段都切不出来,输出 0

样例 #1

样例输入 #1

3 7
232
124
456

样例输出 #1

114

提示

数据规模与约定

对于 100%100\%100% 的数据,有 1n1051\le n\le 10^51≤n≤105,1k1081\le k\le 10^81≤k≤108,1Li108(i[1,n])1\le L_i\le 10^8(i\in[1,n])1≤Li​≤108(i∈[1,n])。

思路

二分答案,即对最长的木头长度二分,对每一次二分得到的mid进行check,如果段数够就往右搜索,因为段数k是一定的,那么每段的长度mid越大,剩下的就越小

int n, k;

bool check(int mid, vector<int> arr)
{
    int s = 0;
    for (int c : arr)
    {
        s += c / mid;
    }
    return s >= k;
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    cin >> n >> k;
    vector<int> arr(n);
    for (int i = 0; i < n; ++i)
    {
        cin >> arr[i];
    }
    sort(all(arr));
    int l = 1, r = arr.back();
    int ans = 0;
    while (l <= r)
    {
        int mid = l + (r - l >> 1);
        if (check(mid, arr))
        {
            l = mid + 1; // 一定是往右搜索,因为段数是一定的,mid越大,剩下的就越小
            ans = max(ans, mid);
        }
        else
            r = mid - 1;
    }
    cout << ans << endl;
    return 0;
}

P2678 跳石头

题目背景

NOIP2015 Day2T1

题目描述

一年一度的“跳石头”比赛又要开始了!

这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选择好了两块岩石作为比赛起点和终点。在起点和终点之间,有 NNN 块岩石(不含起点和终点的岩石)。在比赛过程中,选手们将从起点出发,每一步跳向相邻的岩石,直至到达终点。

为了提高比赛难度,组委会计划移走一些岩石,使得选手们在比赛过程中的最短跳跃距离尽可能长。由于预算限制,组委会至多从起点和终点之间移走 MMM 块岩石(不能移走起点和终点的岩石)。

输入格式

第一行包含三个整数 L,N,ML,N,ML,N,M,分别表示起点到终点的距离,起点和终点之间的岩石数,以及组委会至多移走的岩石数。保证 L1L \geq 1L≥1 且 NM0N \geq M \geq 0N≥M≥0。

接下来 NNN 行,每行一个整数,第 iii 行的整数 Di(0<Di<L)D_i\,( 0 < D_i < L)Di​(0<Di​<L), 表示第 iii 块岩石与起点的距离。这些岩石按与起点距离从小到大的顺序给出,且不会有两个岩石出现在同一个位置。

输出格式

一个整数,即最短跳跃距离的最大值。

样例 #1

样例输入 #1

25 5 2 
2
11
14
17 
21

样例输出 #1

4

提示

输入输出样例 1 说明

将与起点距离为 222 和 141414 的两个岩石移走后,最短的跳跃距离为 444(从与起点距离 171717 的岩石跳到距离 212121 的岩石,或者从距离 212121 的岩石跳到终点)。

数据规模与约定

对于 20%20\%20%的数据,0MN100 \le M \le N \le 100≤M≤N≤10。
对于 50%50\%50% 的数据,0MN1000 \le M \le N \le 1000≤M≤N≤100。
对于 100%100\%100% 的数据,0MN50000,1L1090 \le M \le N \le 50000,1 \le L \le 10^90≤M≤N≤50000,1≤L≤109。

思路

二分答案,根据最多可以搬走的石头数量对每个mid进行check,然后向右搜索

int L, n, m;

bool check(int mid, vector<int> arr)
{
    int cnt = 0; // 为使mid成为可行解,需要搬走的石头数量
    for (int i = 1; i <= n + 1; i++)
    {
        if (arr[i] - arr[i - 1] < mid)
            cnt++, arr[i] = arr[i - 1];
    }
    if (cnt <= m)
        return true;
    return false;
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    cin >> L >> n >> m;
    vector<int> arr(n + 2);
    for (int i = 1; i <= n; ++i)
    {
        cin >> arr[i];
    }
    arr.back() = L;

    int l = 0, r = L;
    int ans = 0;
    while (l <= r)
    {
        int mid = l + (r - l >> 1);
        if (check(mid, arr))
            l = mid + 1, ans = mid;
        else
            r = mid - 1;
    }
    cout << ans << endl;
    return 0;
}

标签:二分,arr,le,int,样例,mid,搜索,答案,ans
From: https://blog.csdn.net/2303_80261633/article/details/142763321

相关文章

  • 二分图的判定-染色法
    二分图如果一张无向图的N个节点可以分成A.B两个不相交的非空集合,并且同一集合内的点之间没有边相连,那么称该无向图为二分图(BipartiteGraph)。定理:二分图不存在奇环(长度为奇数的环)。因为每一条边都是从一个集合走到另一个集合,只有走偶数次才可能回到同一个集合。染色......
  • 【电商搜索】现代工业级电商搜索技术-EMNLP2024-无监督的用户偏好学习
    【电商搜索】现代工业级电商搜索技术-EMNLP2024-无监督的用户偏好学习0.论文信息Title:UnsupervisedHumanPreferenceLearningAuthors:SumukShashidhar,AbhinavChinta,VaibhavSahai,DilekHakkaniTurComments:EMNLP2024MainConferencehttps://arxiv.or......
  • NOIP2024集训Day47 生成树+二分图
    NOIP2024集训Day47生成树+二分图B.[THUPC2022初赛]最小公倍树直接建边显然不行,考虑优化建边。对于两个点\(u,v\),\((u,v)\)的边权为\(\displaystyle\operatorname{lcm}(u,v)=\frac{u\timesv}{\gcd(u,v)}\),显然应该选择\(\gcd(u,v)\)尽可能大的点对连边,也就是......
  • 逆向 Virustotal 搜索接口 X-VT-Anti-Abuse-Header
    搜索示例搜索123,网页地址为:https://www.virustotal.com/gui/search/123/comments请求接口GET/ui/search?limit=20&relationships%5Bcomment%5D=author%2Citem&query=123HTTP/1.1Accept-Encoding:gzip,deflate,br,zstdAccept-Ianguage:en-US,en;q=0.9,es;q=0.8Accep......
  • 3.搜索、模拟
    搜索、模拟\(A\)luoguP1120小木棍\(B\)luoguP2540[NOIP2015提高组]斗地主加强版\(C\)CF58EExpression\(D\)CF293BDistinctPaths\(E\)[ABC352F]EstimateOrder\(F\)[ABC336F]RotationPuzzle\(G\)CF525EAnyaandCubes\(H\)luoguP9234买瓜\(I\)......
  • JAV面试题答案——红黑树怎么保持平衡的
    红黑树根据规则通过旋转和节点染色这两种方式来保持平衡,这些操作是红黑树维持平衡的关键部分。1.旋转操作旋转操作是红黑树维持平衡的主要手段之一,它包括左旋和右旋两种基本操作。旋转操作通常在插入和删除操作中使用,以确保树的性质得以维护左旋将一个节点的右子树提升为其......
  • el-Select组件远程搜索没有箭头图标
    在控制台查看对应的dom,发现使用远程搜索之后,对应的icon的class缺失,将缺失部分的class补全(el-icon-arrow-up)即可tip:只需要找到对应的dom,然后添加class(el-icon-arrow-up)初始化的时候新增下拉箭头的class,当用户触发聚焦的时候,还得添加对应的旋转的class(is-reverse......
  • 闲鱼商品搜索API:提升搜索结果的智能同步
    在互联网时代,二手交易平台日益受到广大消费者的青睐。作为国内领先的闲置交易平台,闲鱼为广大用户提供了丰富的商品资源。为了方便开发者更好地利用闲鱼平台进行应用开发,闲鱼推出了商品搜索Api接口。小编将为您详细介绍这一接口的功能及使用方法。闲鱼商品搜索Api接口,旨在帮助......
  • 搜广推算法校招面试:BOSS直聘 推荐搜索系统工程师
      本文介绍2024届秋招中,BOSS直聘的推荐/搜索系统工程师岗位一面的面试基本情况、提问问题等。  2023年12月,赶在秋招的末尾,投递了BOSS直聘的推荐/搜索系统工程师岗位,并不清楚所在的部门。目前完成了一面,在这里记录一下一面经历。  首先,这一次的投递就是在BOSS直聘这个APP上......
  • 软件测试面试中常见必问(一)内附答案
    一般面试都会按照简历当中我们写的技能或者项目进行提问,所以我们在简历当中一定要写自己能说上来的东西和对简历中的项目一定要有准备。另外,如果真的不知道就请坦诚相待,直说“不好意思,这里我不太清楚”就可以了,有的面试官也会当场告诉你答案。1.自我介绍虽然简历中都有信息,但是......