week5题解

1001

二分模板

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;

int main() {
    int n, m;
    int nb[1000000]; // 存储最多1,000,000个整数

    // 读取元素个数n和查询次数m
    cin >> n >> m;

    // 读取n个已排序的整数,编号从1到n
    for (int i = 1; i <= n; i++) {
        cin >> nb[i];
    }

    // 处理m次查询
    while (m--) {
        int value;
        cin >> value; // 当前查询的整数

        int left = 1;       // 二分查找的左边界
        int right = n;      // 二分查找的右边界
        int state = 0;      // 标记是否找到目标值
        int index = INT_MAX; // 记录找到的最小索引(初始为最大值)

        // 二分查找过程
        while (left <= right) {
            int mid = left + (right - left) / 2; // 防止溢出,计算中间位置

            if (nb[mid] == value) {
                state = 1; // 找到目标值
                if (mid < index) {
                    index = mid; // 更新最小索引
                }
                right = mid - 1; // 继续在左半部分查找是否有更小的索引
            } else if (nb[mid] < value) {
                left = mid + 1; // 目标值在右半部分
            } else {
                right = mid - 1; // 目标值在左半部分
            }
        }

        // 输出结果
        if (state) {
            cout << index << endl; // 找到目标值,输出最小索引
        } else {
            cout << "None" << endl; // 未找到目标值
        }
    }
}

1002

二分模板,逻辑和上一题反一下

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;

int main() {
    int n, m;
    cin >> n >> m;  // 读取序列长度n和查询次数m
    
    vector<int> nums(n);
    for (int i = 0; i < n; i++) {
        cin >> nums[i];  // 读取递减排序的序列
    }
    
    // 处理m次查询
    for (int i = 0; i < m; i++) {
        int target;
        cin >> target;  // 读取当前查询的目标值
        
        int left = 0, right = n - 1;  // 初始化二分查找的左右边界
        int result = -1;  // 初始化结果为-1,表示未找到
        
        // 二分查找循环
        while (left <= right) {
            int mid = left + (right - left) / 2;  // 计算中间位置,防止溢出
            
            if (nums[mid] == target) {
                // 找到目标值,记录位置并继续向右查找(因为序列递减,右边是更小的值)
                result = mid;  // 记录当前位置
                left = mid + 1;  // 向右移动,寻找更大的编号(更靠后的位置)
            }
            else if (nums[mid] > target) {
                // 中间值大于目标值,由于序列递减,目标值在右半部分
                left = mid + 1;
            }
            else {
                // 中间值小于目标值,目标值在左半部分
                right = mid - 1;
            }
        }
        
        // 输出结果
        if (result != -1) {
            // 找到目标值,输出编号(索引+1)
            cout << result + 1 << endl;
        } else {
            // 未找到目标值
            cout << "None" << endl;
        }
    }
}

1003

查找最接近元素

维护最小值和最小距离

关键逻辑

1.当前距离小于历史最小距离->更新最小值和最小距离

2.当前距离等于历史最小距离->更新最小值

3.当前距离大于历史最小距离->去接近目标值

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

const int N = 1e6 + 5;  // 定义数组最大容量

int main() {
    int n, m;
    long long a[N];  
    
    cin >> n;
    for (int i = 0; i < n; i++) {
        cin >> a[i];
    }
    
    cin >> m;
    while (m--) {
        long long x;  
        cin >> x;
        
        int l = 0, r = n - 1;  
        long long min_val, min_dist = LLONG_MAX;  // 最小值和最小距离
        
        while (l <= r) {
            int mid = (l + r) / 2;  // 计算中间位置
            
            // 计算当前元素与目标值的距离
            long long dist = abs(a[mid] - x);
            
            // 更新最小距离和对应的值
            if (dist < min_dist) {
                min_dist = dist;
                min_val = a[mid];
            } 
            // 距离相等时选择较小的值
            else if (dist == min_dist && a[mid] < min_val) {
                min_val = a[mid];
            }
            
            // 调整查找范围
            if (x <= a[mid]) {
                r = mid - 1;  // 目标值在左半部分
            } else {
                l = mid + 1;  // 目标值在右半部分
            }
        }
        
        cout << min_val << endl;
    }
}

1004:

数列分段

最大值最小问题和最小值最大问题通常使用二分答案法,枚举题目需要的的最大值/最小值,然后检查这个值是否满足题目给出的条件,这个最大值和最小值往往和题目逻辑配合,具有递增递减的性质,这样往往采用二分来枚举,抛弃掉无效的选择.

关键点

1.确定好枚举最大值的边界

2.定义好check函数贪心检查满不满足分段的可能

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

int n, m;
vector<int> a;

// 检查当最大段和为x时,是否能分成不超过m段
bool check(int x) {
    int cnt = 1;  // 当前分段数,至少1段
    int sum = 0;  // 当前段的累加和
    
    for (int i = 0; i < n; i++) {
        // 如果当前元素本身已经大于x,肯定不满足条件
        if (a[i] > x) return false;
        
        // 尝试将当前元素加入当前段
        if (sum + a[i] <= x) {
            sum += a[i];  // 可以加入当前段
        } else {
            cnt++;        // 需要开启新段
            sum = a[i];   // 新段从当前元素开始
            
            // 如果分段数已超过m,不满足条件
            if (cnt > m) return false;
        }
    }
    return true;  // 成功分成不超过m段
}

int main() {
    cin >> n >> m;
    a.resize(n);
    
    int l = 0, r = 0;  // 二分查找的左右边界
    
    // 读取数列并确定边界
    for (int i = 0; i < n; i++) {
        cin >> a[i];
        l = max(l, a[i]);  // 左边界:数列最大值(至少一段包含最大元素)
        r += a[i];         // 右边界:数列总和(最多一段包含所有元素)
    }
    
    int ans = r;
    
    // 二分查找最小的最大段和
    while (l <= r) {
        int mid = l + (r - l) / 2;  // 中间值,避免整数溢出
        
        if (check(mid)) {
            // 如果mid满足条件,尝试更小的值
            ans = mid;
            r = mid - 1;
        } else {
            // 如果mid不满足条件,需要更大的值
            l = mid + 1;
        }
    }
    
    cout << ans << endl;
    return 0;
}

1005:

河中跳房子

最小值最大问题,中译中就是我们找到一个值x,然后规定这个x为最小值,在这个x为最小值的情景下,能否满足题目的要求,在该题里题目的限制就是移走的石头数量,x就是两块石头间的最短距离(最短的跳跃距离转换一下),如果x能够满足题目的限制,那么就让规定一个更大的x,看看满不满足题目条件,以此类推,利用二分来枚举这个x.

cpp 复制代码
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int L, N, M;
vector<int> rocks; // 存储岩石位置

// 检查最短跳跃距离为mid时,移走岩石数是否不超过M
bool check(int mid) {
    int last = 0;    // 上一个保留的岩石位置(起点0)
    int removed = 0; // 移走的岩石计数
    
    for (int i = 0; i < N; i++) {
        if (rocks[i] - last < mid) { 
            removed++; // 距离太近,移走当前岩石
        } else {
            last = rocks[i]; // 保留当前岩石,更新上一个位置
        }
    }
    return removed <= M; // 判断移走数量是否允许
}

int main() {
    cin >> L >> N >> M;
    rocks.resize(N);
    for (int i = 0; i < N; i++) {
        cin >> rocks[i];
    }
    
    int left = 1, right = L, ans = 0;
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (check(mid)) {
            ans = mid;   // 当前mid可行,尝试更大值
            left = mid + 1;
        } else {
            right = mid - 1; // 当前mid不可行,尝试更小值
        }
    }
    cout << ans << endl;
    return 0;
}
相关推荐
用户12039112947261 小时前
面试官最爱问的字符串反转:7种JavaScript实现方法详解
算法·面试
vir021 小时前
小齐的技能团队(dp)
数据结构·c++·算法·图论
月夜的风吹雨2 小时前
【C++红黑树】:自平衡二叉搜索树的精妙实现
开发语言·c++·红黑树
讨厌下雨的天空2 小时前
Linux信号
linux·运维·c++
Star在努力2 小时前
C语言复习八(2025.11.18)
c语言·算法·排序算法
赖small强2 小时前
【Linux C/C++开发】第26章:系统级综合项目理论
linux·c语言·c++
南山安2 小时前
从反转字符串看透面试官的“内心戏”:你的算法思维到底怎么样?
javascript·算法·面试
雪不下2 小时前
计算机中的数学:概率(2)
算法
zs宝来了2 小时前
HOT100-二分查找类型题
算法