一.二分查找
首先,话不多说先上模板
1.模板一(查某个数第一次出现的位置)
while (l < r)
{
int mid = (l+r)/2;
if (check(mid)) r = mid; // check()判断mid是否满足性质,满足则返回true
else l = mid + 1;
}
2.模板二(查某个数最后一次出现的位置)
while (l < r)
{
int mid = (l+r+1)/2;
if (check(mid)) l = mid;
else r = mid - 1;
}
注释:两个模板的关键都在4~6行,即如何划分区间。模板一会将区间划为[l, mid]和[mid+1, r][l, mid]和[mid+1, r],而模板二会将区间划为[l, mid−1]和[mid, r][l, mid−1]和[mid, r]。这就是模板最重点的区别。
模板上实际也是可以互换的
-
两个模板最后返回时,都有l==rl==r,且l、rl、r都等于划分的第一个区间的右端点,即mid或mid - 1。
-
模板二求mid时需要+1,这是为了防止死循环。
3.模板三(浮点数)
while(r-l>1e-5) //需要一个精度保证
{
double mid = (l+r)/2;
if(check(mid)) l=mid; //或r=mid;
else r=mid; //或l=mid;
}
然后,来一道简单的模板题练练手P2249 【深基13.例1】查找 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
首先,用stl中的lower_bound秒杀(内核还是二分法)
#include<bits/stdc++.h>
using namespace std;
int a[1000000];
int main() {
int n, m; cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
for (int i = 1; i <= m; i++) {
int x; cin >> x;
int ans = lower_bound(a + 1, a + n + 1, x) - a;//如果找不到则会找到大于它的第一位数
if (a[ans] == x)cout << ans << " ";//判断是否是正确位置
else cout << "-1 ";
}
return 0;
}
二分模板解题
#include<bits/stdc++.h>
using namespace std;
int a[100005];
int main() {
int n, m; cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
for (int i = 1; i <= m; i++) {
int pos; cin >> pos;
int left = 1, right = n;
while (left < right)
{
int mid = (left + right) / 2;
//要求找第一个数字则往前面寻找是否还有,如果没有的话最终循环还是在最初的mid下结束
if (a[mid] >= pos)right = mid;
else left = mid + 1;
}
if (a[left] == pos)cout <<left << " ";//最后left和mid相等
else cout << "-1 ";
}
return 0;
}
因为要求找第一次出现的位置则用模板一,,同时我们也可以举一反三,将代码修改一下就可以找最后一次出现的位置,代码如下
#include<bits/stdc++.h>
using namespace std;
int a[100005];
int main() {
int n, m; cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
for (int i = 1; i <= m; i++) {
int pos; cin >> pos;
int left = 1, right = n;
while (left < right)
{
int mid = (left + right+1) / 2;
if (a[mid] <= pos)left=mid;//注意对比俩者区别
else right=mid-1;
}
if (a[left] == pos)cout << left << " ";
else cout << "-1 ";
}
return 0;
}
相信现在已经渐入佳境了,那再来一道浮点数的二分法,以及用模板如何解答
#include<bits/stdc++.h>
using namespace std;
double sum, back, mon;//back表示每月还多少
double M;
bool check(double mid) {
M = sum;
for (int i = 1; i <= mon; i++) {
M = M + M * mid - back;
}
if (M > 0)return true;//如果大于0,表示还不完,则利率需要减少
else return false;
}
int main() {
cin >> sum >> back >> mon;
double left = 0, right = 100;//right可以尽可能赋值大一点
while (right-left>1e-5)
{
double mid = (left + right) / 2;
if (check(mid))right = mid;//利率将要减少
else left = mid;
}
cout << fixed<<setprecision(1) << left * 100 << endl;
return 0;
}
二.二分答案
这种类型的题一般都会让你求最大的最近距离和最小的最大距离,有点抽象哈,那么先上万能模板再来俩道题练练手就懂了。
模板
while (left <= right)//进行答案二分
{
mid = (left + right) / 2;//假设mid满足则返回true后向右边遍历,反之向左边
if (check(mid)) {
left = mid + 1;
}
else right = mid - 1;
}
P1824 进击的奶牛 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
#include<bits/stdc++.h>
using namespace std;
int n, c, ans;
int a[1000005];
bool check(int mid) {
int now = 1, count = 1;//now表示目前的位置,count表示有几个位置可以满足,且注意起始个数,至少有一个
for (int i = 2; i <= n; i++) {
if (a[i] - a[now] >= mid) {//表示俩者之间的最大距离大于或等于给定答案值则符合
now = i;
count++;
}
}
return count >= c;//如果count大于c(牛的个数)说明此方案可行,返回true
}
int main() {
ans = -1;
cin >> n >> c;//n表示栅栏总数,c表示牛的个数
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
sort(a + 1, a + 1 + n);
int left = 1, right = 10000000, mid;
while (left <= right)//进行答案二分
{
mid = (left + right) / 2;//假设mid满足则返回true后向右边遍历,反之向左边
if (check(mid)) {
left = mid + 1;
ans = max(ans, mid);
}
else right = mid - 1;
}
cout << ans << endl;
return 0;
}
P2678 [NOIP2015 提高组] 跳石头 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
#include<bits/stdc++.h>
using namespace std;
int a[2000000];
int L, N, M;
bool check(int mid) {
int now = 0, count = 0;//一开始在岸上所以初始是没有石头记录,但是注意这里会把最后一个岸算作石头
//所以后面判断条件为count>=x+1;
int x = N - M;//表示余下的石头
for (int i = 1; i <= N + 1; i++) {
if (a[i] - a[now] >= mid) {
now = i;
count++;
if (count >= x + 1)return true;//表示该方案可行
}
}
return false;
}
int main() {
cin >> L >> N >> M;
a[N + 1] = L;
for (int i = 1; i <= N; i++) {
cin >> a[i];
}
int left = 0, right = L,mid,ans=-1;
while (left<=right)
{
mid = (left + right) / 2;
if (check(mid)) { left = mid + 1;
ans = max(ans, mid);
}
else right = mid - 1;
}
cout << ans << endl;
}
再分享几道题,可以看完文章之后自己练练