做网站除了域名还需要什么河南疫情最新消息
一 、 二分算法
二分算法的难点在于对各种各样的细节问题 。所以 , 就算你把二分算法的模板背熟了,但是忽略了各种乱七八糟的边界问题 , 也很难全都AC
那么,我借助下面的OJ题 , 说明一下 需要处理的一些小细节:
34. 在排序数组中查找元素的第一个和最后一个位置 - 力扣(LeetCode)
在查找起始位置的时候 , 需要处理一些细节问题:
- while 循环里面的判断如何写?
- 求中点的方式?
- 二分结束后 , 相遇点的情况是什么?
需要考虑清楚 , 分析明白 , 不要带有未经过 证实的惯性思维!!!!
在查找 终止位置的时候 , 需要处理一些细节问题:
- while 循环里面的判断如何写?
- 求中点的方式?
需要考虑清楚 , 分析明白 , 不要带有未经过 证实的惯性思维!!!!
class Solution {
public:vector<int> searchRange(vector<int>& nums, int target) {int n = nums.size();//处理一下边界情况if(n == 0)return{-1,-1};//1.求起始的位置int left = 0,right = n-1;while(left < right){int mid = (left + right) / 2;if(nums[mid] >= target)right = mid;else left = mid+1;}//left 或者 right 所指的位置就有可能是最终结果if(nums[left]!=target)return{-1,-1};int retleft = left;//记录起始位置//2.求终止位置left = 0,right = n-1;while(left<right){int mid = (left + right+1)/2;if(nums[mid]<=target)left = mid;else right = mid-1;}int retright = right;//记录最终位置return {retleft,retright};}
};
借助上面的题目 , 我们了解了二分算法,这里总结一下:
当我们的解具有二段性的时候 , 就可以使用二分算法找出答案:
1)根据待查找区间的中间位置 , 分析答案会出现在那一侧;
2)接下来舍弃一半的待查找区间 ,转而在有答案的区间内继续使用二分查找结果!
二分的模板在网上至少能搜出来三个以上。但是,我们仅需要掌握一个,并且一直使用下去即可。
下面介绍一种,其他的可以去搜搜看~
1 . 为了防止溢出,求中点时 可以使用下面的方式:
mid = l + ( r - l ) / 2 ;
2. 时间复杂度 : 每次二分都会去掉一般的查找区域(就是 嘎掉一般的数据了...) , 因此时间复杂度 为 log N
3. 【怎么记忆模板?】
不用死记硬背 , 算法原理搞清楚之后 , 在分析题目的时候 , 自然而然就知道怎么写二分的代码了 , 而且 , 在不同的题目 , 边界情况也是不同的 , 需要具体情况具体分析。e
4. 【二分问题解决流程】
1)先画图分析,确定使用左端点模板 还是 右端点模板 , 还是两者配合一起使用
2)二分出结果之后 , 不要忘记判断结果是否存在 , 二分问题细节很多 , 一定一定一定要分析全面!!!
5. 【STL中二分查找】
<algorithm>
1) lower_bound : 大于等于 x 的最小元素 , 返回的是迭代器 , 时间复杂度:O(log N)
2) upper_bound : 大于 x 的最小元素 , 返回的是迭代器 , 时间复杂度:O(log N)
二者均采用二分实现。但是STL中的二分查找 , 只能适用于 “ 在有序的数组中查找 ” ,如果是二分答案就不能使用 。
二、二分查找
2.1 牛可乐与魔法封印
登录—专业IT笔试面试备考平台_牛客网
#include <iostream>using namespace std;const int N = 1e5 + 10;
int a[N];
int n,q;int binary_search(int x,int y)
{//1.找大于等于x 的起始位置int left = 1,right = n;while(left < right){int mid = (right + left)/2;if(a[mid] >= x) right = mid;else left = mid + 1; } //处理数组中不存在大于等于 x 的情况if(a[left] < x) return 0;int tmp = left;//2. 找小于等于y 的终止位置left = 1,right = n;while(left < right){int mid = (left + right + 1 )/2;if(a[mid] <= y) left = mid;else right = mid - 1;} //处理数组中不存在小于等于 y 的情况if(a[left] > y) return 0;return left-tmp+1;
}int main()
{cin >> n;//输入 for(int i = 1 ;i<=n;i++)cin >> a[i];//q次询问cin >> q;while(q--){int x,y;cin >> x >> y;cout << binary_search(x,y) << endl; } return 0;
}
2.2 A-B数对
P1102 A-B 数对 - 洛谷
这里我们用一下STL :
1) lower_bound: 传入要查询区间的左右迭代器 (注意是左闭右开的区间 , 如果是数组就是左右指针 ) 以及要查询的值 k , 然后返回该数组中 >= k 的第一个位置;
2)upper_bound: 传入要查询区间的左右迭代器 (注意是左闭右开的区间 , 如果是数组就是左右指针 ) 以及要查询的值 k , 然后返回该数组中 > k 的第一个位置;
#include <iostream>
#include <algorithm>
using namespace std;typedef long long LL;
const int N = 2E5 + 10;
LL a[N];
LL n,c;int main()
{cin >> n >> c;for(int i = 1;i<=n;i++)cin >> a[i];//1.排序 sort(a+1,a+1+n);//2.b = a - cLL ret = 0;for(int i = 2 ; i<= n ;i++){LL b = a[i] - c;ret += upper_bound(a+1,a+i,b) - lower_bound(a+1,a+i,b);} cout << ret << endl; return 0;
}
2.3 烦恼的高考志愿
P1678 烦恼的高考志愿 - 洛谷
#include <iostream>
#include <algorithm>
using namespace std;typedef long long LL;
const int N = 1e5 + 10;
LL a[N];;
int n,m;int find(LL x)
{int left = 1,right = m;while(left < right){int mid = (left + right)/2;if(a[mid] >= x)right = mid;else left = mid + 1;}return left;
}int main()
{cin >> m >> n;for(int i = 1;i<=m;i++)cin >> a[i];//1.排序sort(a+1,a+m+1);//2.二分//加上左右护法a[0] = -1e7 + 10;LL ret = 0;while(n--){LL x;cin >> x;int pos = find(x);ret += min(abs(a[pos] - x),abs(a[pos-1] - x)); } cout << ret << endl;return 0;
}
三、二分答案
准确来说,应该叫做【二分答案 + 判断】
3.1 木材加工
P2440 木材加工 - 洛谷
#include <iostream>
#include <algorithm>
using namespace std;typedef long long LL;
LL n,k;
const int N = 1e5 + 10;
LL a[N];//当切割长度为 x 的时候,最多能切除多少段
LL calc(LL x)
{ LL ret = 0;for(int i = 1;i<=n;i++){ret += a[i]/x; }return ret;
}
int main()
{cin >> n >> k;//1.输入 for(int i = 1;i<=n;i++)cin>>a[i];//2.排序sort(a+1,a+1+n);//3.二分LL left = 0,right = 1e8;while(left < right){LL mid = (left + right + 1 )/2;LL c = calc(mid);if(c >= k) left = mid;else right = mid-1;}cout << left << endl;return 0;
}
3.2 砍树
P1873 [COCI 2011/2012 #5] EKO / 砍树 - 洛谷
#include <iostream>
#include <algorithm>
using namespace std;typedef long long LL;
const int N = 1e6 + 10;
LL n,m;
LL a[N];LL calc(LL x)
{LL cnt = 0;for(int i = 1;i<=n;i++){if(a[i] > x)cnt += a[i] - x; }return cnt;
}int main()
{cin >> n >> m;//1.输入 for(int i = 1;i<=n;i++)cin >> a[i];//2.排序sort(a+1,a+1+n);//3.二分LL left = 1,right = 2e9;while(left < right){LL mid = (left + right + 1) / 2;LL c = calc(mid);if(c >= m)left = mid;else right = mid - 1; } cout << left << endl;return 0;
}
3.3 跳石头
P2678 [NOIP 2015 提高组] 跳石头 - 洛谷
#include <iostream>
#include <algorithm>
using namespace std;typedef long long LL;
const int N = 5e4 + 10;
LL l,n,m;
LL a[N];//当最短跳跃距离为x , 所能移走的岩石数
LL calc(LL x)
{LL ret = 0;for(int i = 0;i<=n;i++){int j = i+1;while(j <= n && a[j] - a[i] < x)j++;ret += j-i-1;i = j-1;}return ret;
}
int main()
{cin >> l >> n >> m;//1.输入for(int i =1 ;i<=n;i++)cin >> a[i];a[n + 1] = l;n++;//2.二分LL left = 1,right = l;while(left < right){LL mid = (left + right + 1) /2;if(calc(mid) <= m)left = mid;else right = mid - 1; } cout << left << endl;return 0;
}