二分知识点
二分法可应用于
1、从有序数组中快速查找值
2、假定一个解并判断是否可行
3、最大化最小值(以及最小化最大值)
4、最大化平均值
三分知识点
可以应用于:查找一元二次函数的极值
对二分的理解
1、从有序数组中快速查找值
务必是在有序数组中,通过二分查找不断取中点,然后判断中点的值是大于还是小于所要查找的值,进而确定接下来要在哪个区间进行查找,如果等于则找到答案,当然也可能数组中不存在该查找值。
2、假定一个解并判断是否可行
先明确题目要求的解是什么,然后列出在题目限制下,该解可能所在的范围(因为是区间,故必定有序),对这个范围进行二分,取中点,然后根据中点的值推出解为该中点值时,推出此时已知量对应的值,与题目中所给的已知量的值进行对比,看是大了还是小了进而确定解所在的区间,然后继续二分,不断重复,提高解的精度。一般放在循环中二分十多遍就好,太多次会超时。
3、最大化最小值
看到题目的时候没想到能用二分做。
仍然是用第二点用二分对解进行假定并判断解(最小值)是否已经最大,判断部分往往使用贪心法。
4、最大化平均值
对三分的理解
三分法可用于求解一元二次方程的极值问题。函数图像为U字型,我们有一个区间,极值点在区间内,现在要我们找到极值的横坐标。
把这个区间进行三等分,然后看两个等分点对应的函数值,比较函数值的大小,通过比较两个值的大小,我们一定能确定极值点必定在哪个区间。
例如:
对于U型的函数的区间[a,b],极值点在区间内,三等分,得两个等分点:点L和点R.
如果L处对应的函数值比R对应的函数值大,则极值必定在区间[L,b]里,
同理如果L处对应的函数值比R对应的函数值小,则极值必定在区间[a,R]里
然后不断的对新确定的区间进行三分,提高答案精度。
题目
HYSBZ 1734 二分
POJ 1905 二分
HDU 3714 三分
Gym 101375H 二分+交互
HYSBZ 1734 二分
https://www.lydsy.com/JudgeOnline/problem.php?id=1734
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
int n,c,a[100005];
bool judge(int mid)
{
int num=a[0],cnt=1;
for(int i=0;i<n;i++)
if(mid<=a[i]-num){
cnt++;
num=a[i];
}
if(cnt>=c)
return true;
return false;
}
int main()
{
scanf("%d %d",&n,&c);
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
sort(a,a+n);
int l=0,r=a[n-1]-a[0],mid;
while(r-l>=0){
mid=(l+r)/2;
if(judge(mid))
l=mid+1;
else
r=mid-1;
}
printf("%d",l-1);
return 0;
}
POJ 1905 二分
http://poj.org/problem?id=1905
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
double l, c, n, newl, angle, r, h;
bool judge(double mid)
{
r = (mid / 2) + (l*l) / (8 * mid);
angle = asin(l / 2 / r);
double newnewl = 2 * angle*r;
if (newnewl >= newl)
return true;
return false;
}
int main()
{
while (scanf("%lf %lf %lf", &l, &c, &n) && l != -1)
{
newl = (1 + n * c)*l;
double left = 0, right = l / 2, mid = (left + right) / 2;
for (int i = 0; i < 100; i++)
{
if (judge(mid) == true)//h的值偏大
right = mid;
else
left = mid;
mid = (left + right) / 2;
}
h = mid;
printf("%.3f\n", h);
}
return 0;
}
HDU 3714 三分
http://acm.hdu.edu.cn/showproblem.php?pid=3714
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
int T,n,a[10005],b[10005],c[10005];
double f(double x)
{
double ans = a[0]*x*x + b[0]*x + c[0];
for(int i=1; i<n; i++)
ans = max(ans, a[i]*x*x+b[i]*x+c[i]);
return ans;
}
int main()
{
scanf("%d",&T);
while(T--){
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%d%d%d",&a[i],&b[i],&c[i]);
double l=0,r=1000;
for(int i=0; i<100; i++)
{
double l1 = l + (r-l)/3;
double r2 = r - (r-l)/3;
if(f(l1) < f(r2))
r = r2;
else
l = l1;
}
printf("%.4lf\n",f(l));//最后l/r所在的为最小值的x
}
return 0;
}
Gym - 101375H
http://codeforces.com/gym/101375/problem/H
思路
题目要你找到计算机设定好的数字,每次询问都会告诉你你的猜测是偏大还是偏小了,一直二分找到答案就好了。
代码中需要注意一下的是
l=mid+1;
而
r=mid;
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iostream>
using namespace std;
int main()
{
char ch;
int l=1,r=1e9,mid=(r+l)/2;
for(int i=0;i<50;i++)
{
printf("Q %d\n",mid);
cout.flush();
scanf("%s",&ch);
if(ch=='>')
l=mid+1;
if(ch=='<')
r=mid;
if(ch=='=')
break;
mid=(l+r)/2;
}
return 0;
}