二分
二分思想,是一种重要的思想,它的优点在于,对于大量数据处理的时候,我们能有效地简化算法,减少时间复杂度,提高效率。
以我们的二分查找为例,在一个有序数列中查找某个数的下标,以我们以前的方法,遍历整个数列,直到找到这个数为止,如果数据量小的话,这个方法是可行的,但是到了上亿甚至更大的数据量,这个算法就过于复杂。我们这时就可以用二分查找的方法,所谓二分查找,以此题为例(升序),我们先找到所有数最中间的那个数,与我们的目标数进行比较,如果目标数大于这个中间数,就在中间数到最后一个数之间再找一个中间数,与目标数比较,复杂度是logn,由于是指数级的增长,大大减少了比较次数,可能上亿的数据量我们只需要比较几十次就可以达到目的,这就是二分的核心思想。
先看一个简单的例题:
现有 N 个大理石,每个大理石上写了一个非负整数。首先请你将这 N 块大理石按照它们上面所写数字的大小从小到大排序,然后按照排序后的顺序把这 N 个大理石从 1 ~ N 编号。之后请你回答 Q 个询问,每个询问问是否存在一个大理石上写着某个整数 x,如果存在,请你回答写着整数 x 的编号最小的大理石的编号,如果不存在,请你输出 x not found。
有多组数据。
每组数据第一行包括两个整数 N,Q,当输入为 N = 0, Q = 0 时,表示结束。
否则接下来 Q 行,每一行包括一个数字 x,表示询问。
Sample input
4 1
2
3
5
1
5
5 2
1
3
3
3
1
2
3
0 0
Sample Output
CASE# 1:
5 found at 4
CASE# 2:
2 not found
3 found at 3
这个题目过于简单了,直接看代码:
#include<iostream>
#include<algorithm>
using namespace std;
int a[10010], b[10010];
int main()
{
int n, q, num=1, l, r, m;
while(cin >> n >> q)
{
if(n==0&&q==0)
return 0;
for(int i = 1; i <= n; i++)
cin >> a[i];
for(int i = 1; i <= q; i++)
cin >> b[i];
sort(a+1,a+n+1);
printf("CASE# %d:\n",num++);
for(int i = 1; i <= q; i++)
{
l = 1, r = n, m = (l+r)/2;
while(1)
{
if(b[i] <= a[m])
{
r = m;
if(r == l + 1)
{
if(b[i] == a[l])
{
cout << b[i] <<" found at " << l <<endl;
break;
}
else if(b[i] == a[r])
{
cout << b[i] <<" found at " << r << endl;
break;
}
else
{
cout << b[i] <<" not found" << endl;
break;
}
}
}
else
{
l = m;
if(r == l + 1)
{
if(b[i] == a[l])
{
cout << b[i] <<" found at " << l <<endl;
break;
}
else if(b[i] == a[r])
{
cout << b[i] <<" found at " << r << endl;
break;
}
else
{
cout << b[i] <<" not found" << endl;
break;
}
}
}
m = (l + r)/2;
}
}
}
}
我们再来看一个二分来解决函数的最值问题的例题:
某学长最近在玩一款名为“我的世界”的一款游戏。众所周知,这个游戏中有很多水池,今天学长出门寻宝的过程中发现了一个水池,水池的水位在不断的变化,他想了解一下这个水池的水位是如何变化的,他去”游戏百科“中搜了一下,果然”游戏百科“真强大,他了解到水位是根据: F(x) = 6x^7 +8x^6 +7x^3 +5x^2-y*x (0 <= x <=100) 变化的,其中y值是学长的游戏人物的生命值。学长想知道当自己游戏人物的生命值一定时,F(x)的最小值(0 <= x <=100)
input
第一行包含一个整数Y(1<=T<=100) ,代表测试样例的个数。接下来输入包含T行,每行一个实数y,代表当前学长游戏人物的生命值(0 < y <1e10)。
output
输出一行包含一个实数F(x)(0 <= x <=100),代表最低水位,保留4位小数
Sample Input
2
100
200
Sample Output
-74.4291
-178.8534
就是一个求最小值问题啦,高中数学知识,我们先求导,如果导数恒正或者恒负,那么就在端点处取得最小值,由于在此题中导数的变化是递增的,排除前面两种情况之后就只能是导数为零的点处取得最小值,那么就可以用二分查找的方法找到自变量的值,然后代入函数,就是最小值了。
#include<iostream>
#include<cmath>
using namespace std;
double dao(double x)
{
return 42*pow(x,6) + 48*pow(x,5) + 21*pow(x,2) + 10*x;
}
double yuan(double x,double y)
{
return 6*pow(x,7) + 8*pow(x,6) + 7*pow(x,3) + 5*pow(x,2) - y*x;
}
int main()
{
int t;
cin >> t;
double l, r, m, min_, y, x;
while(t--)
{
cin >> y;
if(y <= 0)//递增
min_ = 0;
else if(dao(100) - y <= 0)
min_ = yuan(100, y);
else
{
l = 0, r = 100, m = 50;
while(fabs(l - m) > 1e-5)
{
x = dao(m);
if(x > y)//导数大于0
r = m;
else
l = m;
m = (l + r)/2;
}
min_ = yuan(m,y);
}
printf("%.4f\n",min_);
}
return 0;
}
最后我们看一个比较复杂的问题:
我的生日要到了!根据习俗,我需要将一些派分给大家。我有 N 个不同口味、不同大小的派。有 F 个朋友会来参加我的派对,每个人会拿到一块派 (必须一个派的一块,不能由几个派的小块拼成;可以是一整个派)。
我的朋友们都特别小气,如果有人拿到更大的一块,就会开始抱怨。因此所有人拿到的派是同样大小的 (但不必是同样形状的),虽然这样有些派会被浪费,但总比搞砸整个派对好。当然,我也要给自己留一块,而这一块也要和其他人的同样大小。
请问我们每个人拿到的派最大是多少?每个派都是一个高为 1,半径不等的圆柱体。
input
首先输入一个整数 T,表示测试数据的组数。
对于每组测试数据:
第 1 行包含两个正整数 N, F,分别表示派的数量和朋友的数量,满足 1 <= N, F <= 10000。
第 2 行包含 N 个 1 到 10000 之间的整数,表示每个派的半径。
output
每组测试数据对应一行,输出每个人能得到的最大的派的体积,误差不超过 10^(-3)。
sample input
3
3 3
4 3 3
1 24
5
10 5
1 4 2 3 4 5 6 5 4 2
sample output
25.1327
3.1416
50.2655
一看到这个问题,似乎很简单,但是如何代码实现却成了问题,我们可以用二分的思想,比如,每个人都分最大的能不能分下去,每个人都分最大的一半,能不能分下去,四分之一能不能分下去…
有了这个思路,我们就可以写出我们的代码了:
#include<iostream>
#include<algorithm>
using namespace std;
const double PI = 3.141592653589793;
double a[10010];
double f, rr, r, m ,l;
int n;
bool dis(double x)
{
int num = 0;
for(int i = 1; i <= n; i++)//分派的数量能够达到人数要求
{
num += (int)(a[i]/x);
}
if(num >= f)
return true;
else
return false;
}
int main()
{
int t;
cin >> t;
while(t--)
{
cin >> n >> f;
f++;
for(int i = 1; i <= n; i++)
{
cin >> rr;
a[i] = rr * rr * PI * 100000;将数据扩大处理,以免丢失精度
}
sort(a+1,a+1+n);//排序
r = a[n];
l = 1;
m = (r + l)/2;
while(r > l + 1)二分
{
if(dis(m))
l = m;
else
r = m;
m = (l + r)/2;
}
printf("%.4f\n",m/100000);
}
return 0;
}
注:这题在oj上判的话,我们的π的小数位数要多一点,不然可能不不能AC。
本文深入解析二分法的原理与优势,通过多个实例展示其在数据查找、函数最值及复杂问题求解中的应用,代码实现详尽,助你掌握高效算法。
1714

被折叠的 条评论
为什么被折叠?



