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;
}