二分答案
大佬是这样解释的☟
原博客
以下添加了一些个人理解
1.使用场景
二分答案一般使用在求解符合条件的最小值或者最大值上面,当我们遇到这两个问题的时候,一般都可以使用二分答案来解决问题。(我觉得这段讲的不是很好)
- 条件:所求的答案具有单调性,可以枚举答案,能判断答案的可行性
- 特征:①求xxx最大值最小 ② 求xxx最小值最大.③ 使最近的距离最大 etc
2.什么是二分答案
二分答案就是通过对所有可能的答案区间进行折半查找,不断缩减范围,最终确定答案的方法。
3.求最小值
//求最小值
int getAnswer(int l, int r) {
int mid;
while(l < r) {
mid = (r + l) / 2;
if(check(mid)) {
r = mid;
}
else {
l = mid + 1;
}
}
}
我们可以知道,要求最小值,那么所满足条件的值赋值给右边界,不满足的值加一赋值给左边界,当l等于r或者l>r时,则说明已经查询到符合的最小值。
4.求最大值
int getAnswer(int l, int r) {
int mid;
while (l<r) {
mid = (r + l + 1) / 2;
if (check(mid)) {
l = mid;
}
else {
r = mid - 1;
}
}
}
同样,要求最大值,那么就需要舍弃符合条件的较小的值,即将左边界赋值符合条件的点,右边界赋值不符合条件的值减一。不断查询下去,则最后剩下符合条件最大值。其中mid = (r + l + 1) / 2是为了防止无限循环,因为当l=r+1时刚好符合条件,r+l/2=l,就会无限循环。
5. check函数构思
我们既然选择了使用二分法来求解,那么我们在check的时候,应该是在我们已知答案的情况下去确认答案,即不是从一般思考方向设想,而是验证。check函数往往是基于假定知道答案,来验证是否正确。这样做,你的代码的时间复杂度会减小不少
写了个傻不拉几的东西 PS:不保证是对的…
#include <iostream>
using namespace std;
int des;
int check(int a)
{
if (a > des) return 0;//mid大于desR左移
return 1;
}
int binary(int l, int r)
{
while (l < r)
{
printf("%d: %d\n", l, r);
int mid = (l + r + 1) / 2;
if (check(mid)) l = mid;
else r = mid - 1;
}
return r;
}
int main()
{
cin >> des;
//自动猜数
cout<<binary(1, 1000);
system("pause");
return 0;
}
大概知道是怎么回事了就可以练练题了
- 洛谷 P1182 数列分段
- 洛谷 P1316 丢瓶盖
- 洛谷 P1577 切绳子 (这题有点点坑)
Talk is cheap ,show me the code.
1 数列分段
#include <iostream>
#include <fstream>
#include <algorithm>
using namespace std;
int n,m;
const int N = 100100;
int a[N];
bool check(int size)
{
int group = 1, res = size;
for (int i = 1; i <= n; i++)//每次给size大小,看能构成几个组
{
if (res >= a[i])res -= a[i];//每次就减a[i]
else group++, res = size - a[i];//不够就置为size 再减去a[i]
}
//cout << group << " ";
return group <= m;
}
int main()
{
cin >> n >> m;
int sum=0;
int l = 0;
for(int i = 1; i <= n; i++)
{
cin >> a[i];
sum += a[i];
l = max(l, a[i]);
}
int r = sum, ans = 0;
//二分答案
while (l < r)
{
int mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid+1;
}
cout << r<<endl;
// system("pause");
return 0;
}
2.丢瓶盖
//稍稍有些不一样,但是思路是一样的
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
const int N = 100010;
ll a[N];
int n, m;
bool check(ll size)
{
int cnt = 1;
int j = 1;
for (int i = 2; i <= n; i++)
{
if (a[i] - a[j] >= size)
{
cnt++;
j = i;
}
}
return cnt >= m;
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i++)
cin >> a[i];
sort(a + 1, a + n + 1);
ll l = 0, r = a[n] - a[1];
while (l < r)
{
ll mid = floor((l + r) >> 1);
if (check(mid)) l = mid+1;
else r = mid;
}
cout << r-1 << endl;
// system("pause");
return 0;
}
3.坑坑坑!!!切绳子
此题甚是坑,主要集中在 与别的二分不一样的地方
别的二分因为是整数类型的,所以对l,r 需要对其中一个加一
此题不怎么一样这是double类型的,可以直接令l=mid,r=mid。。。。。。
但是他最后是取的是,符合条件的最大值所以就取r,不能取l,mid。你想问为什么,TMD我一个一个错出来的。。。。
而且在最后输出的时候还需要舍弃小数点2位后的值
#include <iostream>
#include <fstream>
#include <iomanip>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
const int N = 100010;
double a[N];
int n, m;
bool check(double size)
{
int cnt = 0;
for (int i = 1; i <= n; i++)
{
if (a[i] >= size)
{
cnt = cnt + floor(a[i] / size);//size大小的条数
}
}
//cout << cnt << endl;
return cnt >= m;
}
int main()
{
cin >> n >> m;
double r=0x3f3f3f3f,l=0;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
cout.setf(ios::fixed);
double mid;
while (r-l>1e-6)
{
mid = (l + r) / 2.0;
//cout << mid << endl;
if (check(mid))
l = mid;
else
r = mid;
//cout <<setprecision(2)<<"l "<<l<<" R "<< r << endl;
//cout << endl;
}
cout << setprecision(2) << floor(r*100)/100 << endl;
//printf("%.2f\n", floor(l * 100) / 100);
// system("pause");
return 0;
}
/*
4 11
8.02
7.43
4.57
5.39
ans:2.00
*/
有东西后续再补