【C++ 算法练习题】二分法例题与解析

问题一:开花

问题描述

小A所在的学校又迎来了一年一度的开花活动,有 n 名学生被评为文学优秀奖,m 名学生被评为体育优秀奖。现已知两个奖项获奖同学的编号,每个同学都有唯一的编号。只有同时被评为文学优秀奖和体育优秀奖的学生才能开花,小A想知道开花的名单,请你帮他统计一下。

输入格式

第一行两个整数 n,m\ (1\le n,m \le 10^5) ,分别表示文学优秀奖和体育优秀奖的获奖人数。

第二行 n 个不同的整数,表示获得文学优秀奖的同学编号。

第二行 m 个不同的整数,表示获得体育优秀奖的同学编号。

所有编号为正整数且不超过 10^9

输出格式

一行若干个空格分隔的整数,表示开花的同学编号,按文学优秀奖的先后次序输出。

样例输入

4 4
5 1 7 3
2 3 4 1

样例输出

1 3

问题解析

也就是对每个文学优秀奖的获得者,在体育优秀奖中查找即可。也就是在一个数组中找一个数,也就转化为二分搜索问题。

二分搜索代码如下:(应该记住)

int binary_search(int x, int l, int r){
    while(l <= r){
        int mid = (l + r) >> 1;
        if(t[mid] == x){
            return mid;
        }
        if(t[mid] < x){
            l = mid + 1;
        }else{
            r = mid - 1;
        }
    }
    return -1;
}

代码

#include<bits/stdc++.h>
using namespace std;
int t[100005];
int w[100005];
int binary_search(int x, int l, int r){
    while(l <= r){
        int mid = (l + r) >> 1;
        if(t[mid] == x){
            return mid;
        }
        if(t[mid] < x){
            l = mid + 1;
        }else{
            r = mid - 1;
        }
    }
    return -1;
}
int main(){
    int n, m;
    cin >> n >> m;
    for(int i = 0; i < n; ++i){
        cin >> w[i];
    }
    for(int i = 0; i < m; ++i){
        cin >> t[i];
    }
    sort(t, t + m);
    int t = 0;
    for(int i = 0; i < n; ++i){
        if(binary_search(w[i], 0, m - 1) != -1){
            t++;
            if(t == 1) cout << w[i];
            else{
                cout << " " << w[i];
            }
            
        }
    }
    return 0;
}

问题二:切割钢管

问题描述

小A是某工地的计算工程师。工地现有 n 根钢管,第 i 根钢管的长度为 a_i​

现在想用这 n 根钢管来做一个支撑用的柱子。我么可以切割这些钢管成为更短的钢管,但是不能缝合两根钢管。为了安全起见,柱子必须用 至少 k 根长度相同的钢管加上混凝土制成,并且要求钢管长度必须为 整数

小A想知道,这个柱子最高能建成多高(钢管可以有剩余)。

输入格式

输入第一行一个整数 n, k (1 \le n, k \le 10000)

接下来一行输入 n 个空格隔开的整数 l_i(1 \le l_i \le 10^8),表示每根钢管的长度。

输出格式

输出最大的高度。

样例输入1

2 4
8 4

样例输出1

2

样例输入2

8 8
12 3 14 12 14 20 4 8

样例输出2

7

问题分析

这里我们对答案进行二分,找到一个可行的答案即可!最后输出的是 r - 1,因为我们保证的是 l 是可行的范围, r 是不可行的范围,最后需要减一,使得答案是可行的。

代码

#include<bits/stdc++.h>
using namespace std;
int a[10010];
int main(){
    int n, k;
    cin >> n >> k;
    for(int i = 0; i < n; i++){
        cin >> a[i];
    }
    int l = 0, r = 100000001;
    while(l < r){
        int mid = (l + r) >> 1;
        int cnt = 0;
        for(int i = 0; i < n; i++){
            cnt += a[i] / mid;
        }
        if(cnt >= k){
            l = mid + 1;
        }else{
            r = mid;
        }
    }
    cout << r - 1;
    return 0;
}

问题三:跳石头

问题描述

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

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

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

输入格式

输入第一行包含三个整数 L,N,M,分别表示起点到终点的距离,起点和终点之间的岩石数,以及组委会至多移走的岩石数。

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

输出格式

输出只包含一个整数,即最短跳跃距离的最大值。

数据范围

对于 20% 的数据,0 \le M \le N \le 10

对于 50% 的数据,0 \le M \le N \le 100

对于 100% 的数据,0 \le M \le N \le 50,0001 \le L \le 1,000,000,000

样例说明

将与起点距离为 2 和 14 的两个岩石移走后,最短的跳跃距离为 4(从与起点距离 17 的岩石跳到距离 21 的岩石,或者从距离 21 的岩石跳到终点)。

样例输入

25 5 2
2
11
14
17
21

样例输出

4

问题分析

对于这些类似 “最短最长”、“最小最大” 等题目,我们经常可以使用二分答案来验证即可。

 我们先看看对答案怎么判断可行的?

如果一个答案 x 可行,它要满足在跳跃的距离最短为 x 的时候,尽可能少移走石头。于是,我们统计在 x 的情况下,移走石头的数量 cnt 与题目要求的移走石头的数量 m 进行对比即可。now 代表当前所在的石头号,通过比对,如果发现间隔小于 x ,则必须进行一次移走石头,否则不能保证最短跳跃距离为 x。

bool check(int x){
    //now是当前站在哪
    //cnt是必须移走多少块石头
    int cnt = 0, now = 0;
    //从当前站的位置,考虑面前的每一块石头
    for(int i = 1; i <= n + 1; i++){
        //如果石头间的距离比 x 要小,则需要移走,因为如果不移走,这次跳跃的距离就短了。
        if(stone[i] - stone[now] < x){	
            cnt++;
        } else{
            //距离够的话,考虑跳下一步
            now = i;
        }
    }
    //cnt计算的是,在我们固定了跳多远的情况下,移走尽可能少的石头。
    return cnt <= m;
}

下一步,我们需要对可能的答案进行二分查找即可。

代码


#include<bits/stdc++.h>
using namespace std;
int stone[50002];
int l, n, m;
bool check(int x){
    //now是当前站在哪
    //cnt是必须移走多少块石头
    int cnt = 0, now = 0;
    //从当前站的位置,考虑面前的每一块石头
    for(int i = 1; i <= n + 1; i++){
        //如果石头间的距离比 x 要小,则需要移走,因为如果不移走,这次跳跃的距离就短了。
        if(stone[i] - stone[now] < x){	
            cnt++;
        } else{
            //距离够的话,考虑跳下一步
            now = i;
        }
    }
    //cnt计算的是,在我们固定了跳多远的情况下,移走尽可能少的石头。
    return cnt <= m;
}
int binary_search(int l, int r){
    int ans;
    while(l <= r){
        int mid = (l + r) >> 1;
        if(check(mid)){
            ans = mid;
            l = mid + 1;
        } else{
            r = mid - 1;
        }
    }
    return ans;
}
int main(){
    cin >> l >> n >> m;
    //stone[0] = 0
    //从 1 开始,方便我们每一步跳
    for(int i = 1; i <= n; i++){
        cin >> stone[i];
    }
    //把最后一个填充
    stone[n + 1] = l;
    cout << binary_search(1, l);
    return 0;
}

问题四:转圈游戏

问题描述

n 个小伙伴(编号从 0 到 n-1)围坐一圈玩游戏。按照顺时针方向给 n 个位置编号,从 0 到 n-1。最初,第 0 号小伙伴在第 0 号位置,第 1 号小伙伴在第 1 号位置,……,依此类推。

游戏规则如下:每一轮第 0 号位置上的小伙伴顺时针走到第 m 号位置,第 1 号位置小伙伴走到第 m+1 号位置,……,依此类推,第 n−m 号位置上的小伙伴走到第 0 号位置,第 n−m+1 号位置上的小伙伴走到第 1 号位置,……,第 n−1 号位置上的小伙伴顺时针走到第 m−1 号位置。

现在,一共进行了 10^k 轮,请问 x 号小伙伴最后走到了第几号位置。

输入格式

输入共 1 行,包含 4 个整数 n、m、k、x,每两个整数之间用一个空格隔开。

输出格式

输出共 1 行,包含 1 个整数,表示 10^k 轮后 x 号小伙伴所在的位置编号。

数据范围

对于 30% 的数据,0<k<7

对于 80% 的数据,0 < k < 10^7

对于 100% 的数据,1<n<1,000,0000 <m <n0 \le x \le n0 < k< 10^9

样例输入

10 3 4 5

样例输出

5

问题分析

他们围成一个圈,走第一轮, x 会走到 x + m 模 n 号,走第二轮, x 会走到 x + 2 * m 模 n 号,以此类推,第10的k次方,会走到 x + 10^k * m 模 n。难点就在于求10的k次方。

快速幂

LL pow_mod(LL a, LL b){
    LL res = 1;
    while(b > 0){
        if(b & 1){
            res = res * a % n;
        }
        a = a * a % n;
        b >>= 1;
    }
    return res;
}

代码

#include<bits/stdc++.h>
using namespace std;
int n, m, k, x;
typedef long long LL;
LL pow_mod(LL a, LL b){
    LL res = 1;
    while(b > 0){
        if(b & 1){
            res = res * a % n;
        }
        a = a * a % n;
        b >>= 1;
    }
    return res;
}
int main(){
    cin >> n >> m >> k >> x;
    cout << (x + pow_mod(10, k) * m % n) % n;
    return 0;
}

 

  • 6
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是C++实现顺序查找算法二分法查找算法的示例代码: ```c++ #include <iostream> #include <vector> #include <algorithm> using namespace std; // 顺序查找算法 int seqSearch(vector<int>& nums, int target) { for (int i = 0; i < nums.size(); i++) { if (nums[i] == target) { return i; } } return -1; // 查找失败 } // 二分法查找算法 int binarySearch(vector<int>& nums, int target) { int left = 0, right = nums.size() - 1; while (left <= right) { int mid = (left + right) / 2; if (nums[mid] == target) { return mid; } else if (nums[mid] < target) { left = mid + 1; } else { right = mid - 1; } } return -1; // 查找失败 } int main() { vector<int> nums = {3, 5, 2, 8, 4, 7, 1, 6}; sort(nums.begin(), nums.end()); // 二分法查找算法需要有序表 int target = 4; int index1 = seqSearch(nums, target); // 调用顺序查找算法 int index2 = binarySearch(nums, target); // 调用二分法查找算法 if (index1 != -1) { cout << "顺序查找成功,目标元素下标为:" << index1 << endl; } else { cout << "顺序查找失败,未找到目标元素" << endl; } if (index2 != -1) { cout << "二分法查找成功,目标元素下标为:" << index2 << endl; } else { cout << "二分法查找失败,未找到目标元素" << endl; } return 0; } ``` 以上代码中,我们使用了STL中的vector容器来存储顺序表元素,并使用sort函数对其进行排序,以便二分法查找算法能够正确执行。在main函数中,我们分别调用了顺序查找算法二分法查找算法,查找目标元素的值为4。最后,根据返回的下标值,输出查找结果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

见见大魔王

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值