二分答案 二分查找

二分答案
大佬是这样解释的
原博客
以下添加了一些个人理解
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;
}

大概知道是怎么回事了就可以练练题了

  1. 洛谷 P1182 数列分段
  2. 洛谷 P1316 丢瓶盖
  3. 洛谷 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

*/

有东西后续再补

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值