继续闲聊:记得蒋大佬跟我说过那个二分都不能叫算法,一写就写出来了,啊啊啊啊,感觉跟人家的水平确实有所差距,当时真的连二分算法都写不出来,现在呢,虽然没有特别的精通,但好歹是能写出来了,那就说一下我对二分的总结吧。
1.二分的模板
bool check(int x){}//重点是将check函数写出来
int ans = -1;
int low=1;
int high = n;
while (low <= high) {
int mid = (low + high) / 2;
if (check(mid)) {
ans = mid;
low = mid + 1;//比它更优的都在它的右边
}
else {
high = mid - 1;//说明此时条件大了,不满足当前出口的条件,所以向左查找。
}
}
return ans;//返回ans
2.二分查找(OlogN)
上代码:
#include <iostream>
using namespace std;
int n;int m;
int a[100];
int ans = -1;
int binary_search(int a[], int n, int x) {
int left = 1;
int right = n;
while (left <= right) {
int mid = (left + right) / 2;
if (a[mid] ==x) {//边界的条件,符合这个数就可以停止查找了
ans = mid;
break;
}
else if (a[mid] < x) {
left = mid + 1;
}
else {
right = mid - 1;
}
}
return ans;
}
int main()
{
cin >> n>>m;
for (int i = 1; i <=n; i++) {
cin >> a[i];
}
binary_search(a, n, m);
cout << ans << endl;
}
完美输出!
借鉴了五点七边的“二分查找为什么总是写错”更新之后代码为:(赖大佬推荐的)二分查找为什么总是写错?_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1d54y1q7k7
#include <iostream>
using namespace std;
int n; int m;
int a[100];
int ans = -1;
int binary_search(int a[], int n, int x) {
int left =0;//搜索的第一个数字为1时,左边界定义为0即可,这样的话不会溢出
int right = n;
//不会出错的二分模板
while ((left + 1) != right) {
int mid = (left + right) / 2;
if (a[mid] == x) {//边界的条件,符合这个数就可以停止查找了
ans = mid;
left = mid;
}
else {
right = mid;
}
}
return ans;
}
int main()
{
cin >> n >> m;
for (int i = 1; i<=n; i++) {
cin >> a[i];
}
binary_search(a, n, m);
cout << ans << endl;
}
2.二分答案
例题开始:
P2440 木材加工
题目背景
要保护环境
题目描述
木材厂有 n根原木,现在想把这些木头切割成 k 段长度均为 l 的小段木头(木头有可能有剩余)。
当然,我们希望得到的小段木头越长越好,请求出 l 的最大值。
木头长度的单位是 cm,原木的长度都是正整数,我们要求切割得到的小段木头的长度也是正整数。
例如有两根原木长度分别为 11 和 21,要求切割成等长的 66 段,很明显能切割出来的小段木头长度最长为 5。
输入格式
第一行是两个正整数 n,kn,k,分别表示原木的数量,需要得到的小段的数量。
接下来 nn 行,每行一个正整数 L_iLi,表示一根原木的长度。
输出格式
仅一行,即l的最大值。
如果连 1cm 长的小段都切不出来,输出 0
。
输入输出样例
输入
3 7
232
124
456
输出
114
P2440 木材加工 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)https://www.luogu.com.cn/problem/P2440
题目分析:二分的模板是固定的,主要是要写出什么?那就是check函数,那该怎么写?
首先是个数一定要>=所给的段数,然后找到它的一个边界值,然后这个L到r的取值该如何?
L=1(因为题目中明确说明了不得小于1cm),那最大的边界定为多少呢?当然是所给木块长度的最大值(
说不定它就让你取一个呢),所以就有一个bool函数:
bool check(int mid) {
long long sum = 0;//数据比较大
for (int i = 1; i <= n; i++) {
sum += len[i] / mid;
}
return sum >= k;//大于等于k才是正确的
}
于是就套模板呗:(本人不喜欢伪代码,代码写出来就是让人看得懂,觉得美)
#include <iostream>
#include <algorithm>
using namespace std;
int n; int k;
int len[100100];
bool check(int mid) {
long long sum = 0;//数据比较大
for (int i = 1; i <= n; i++) {
sum += len[i] / mid;
}
return sum >= k;
}
int main()
{
cin >> n >> k;
int ans = 0;
int maxn=-100;
for (int i = 1; i <= n; i++) {
cin >> len[i];
maxn = max(maxn, len[i]);
}
int low = 1;
int high = maxn;
while (low <= high) {
int mid = (low + high) / 2;
if (check(mid)) {
ans = mid;
low = mid + 1;//向右边搜索,试图去找到更优的解
}
else {
high = mid - 1;//条件不符合,向左边搜寻能满足条件的数
}
}
cout << ans << endl;
}
更新后:
#include <iostream>
#include <algorithm>
using namespace std;
int n; int k;
int len[100100];
bool check(int mid) {
long long sum = 0;//数据比较大
for (int i = 1; i <= n; i++) {
sum += len[i] / mid;
}
return sum >= k;
}
int main()
{
cin >> n >> k;
int ans = 0;
int maxn = -100;
for (int i = 1; i <= n; i++) {
cin >> len[i];
maxn = max(maxn, len[i]);
}
int low =0;//此时为什么要标记成0,因为最小的条件为1,比最小的条件减一就不会溢出
int high = maxn;
while (low+1!=high) {//无非就是两个指针的移动
int mid = (low + high) / 2;
if (check(mid)) {//符合条件的进入
ans = mid;
low = mid;//向右边搜索,试图去找到更优的解
}
else {
high = mid;//条件不符合,向左边搜寻能满足条件的数
}
}
cout << ans << endl;
}
所以总结下来改进的模板就是:
int left =0;//搜索的第一个数字为1时,左边界定义为0即可,这样的话不会溢出
//int left=-1;//搜索的第一个数为0时,定义左边界为-1
int right = n;
//不会出错的二分模板
while ((left + 1) != right) {
int mid = (left + right) / 2;
if (a[mid] == x) {//边界的条件,符合这个数就可以停止查找了
ans = mid;
left = mid;
}
else {
right = mid;
}
}
return ans;
大大保证了二分算法不会出错,目前二分的掌握暂时性到这里,以后会更新二分查找中浮点数的该如何去写,感谢各位大佬们的鼎力相助,才让本菜菜得以掌握二分的基础,感谢观看!