知识点一 搜索:二分法+三分法+二分交互(HYSBZ 1734+POJ 1905+HDU 3714+Gym - 101375H)

二分知识点

二分法可应用于
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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. 二分法: 首先确定初始区间 [a,b],然后根据函数值与零点的关系缩小区间范围,最终得到一个满足精度要求的零点。 完整代码如下: function x = bisection(f, a, b, tol) % f: 待求解方程的函数句柄 % a, b: 初始区间 % tol: 误差容限 if nargin < 4 tol = 1e-6; end if f(a) * f(b) >= 0 error('初始区间无法确保方程有唯一实根'); end while abs(b - a) > tol c = (a + b) / 2; if f(c) == 0 x = c; return; elseif f(c) * f(a) < 0 b = c; else a = c; end end x = (a + b) / 2; 使用方法: >> f = @(x) log10(x) - 2; >> bisection(f, 1, 10) ans = 100.0000 2. 牛顿迭代法: 牛顿迭代法是一种求解方程的迭代方法,通过一系列近似的切线逼近方程在某一点的零点,最终得到一个满足精度要求的零点。 完整代码如下: function x = newton(f, df, x0, tol) % f: 待求解方程的函数句柄 % df: f 的一阶导数函数句柄 % x0: 初始点 % tol: 误差容限 if nargin < 4 tol = 1e-6; end while true fx = f(x0); dfx = df(x0); x = x0 - fx / dfx; if abs(x - x0) < tol break; end x0 = x; end 使用方法: >> f = @(x) log10(x) - 2; >> df = @(x) 1 / (x * log(10)); >> newton(f, df, 10) ans = 100.0000 完整代码: function x = bisection(f, a, b, tol) % f: 待求解方程的函数句柄 % a, b: 初始区间 % tol: 误差容限 if nargin < 4 tol = 1e-6; end if f(a) * f(b) >= 0 error('初始区间无法确保方程有唯一实根'); end while abs(b - a) > tol c = (a + b) / 2; if f(c) == 0 x = c; return; elseif f(c) * f(a) < 0 b = c; else a = c; end end x = (a + b) / 2; end function x = newton(f, df, x0, tol) % f: 待求解方程的函数句柄 % df: f 的一阶导数函数句柄 % x0: 初始点 % tol: 误差容限 if nargin < 4 tol = 1e-6; end while true fx = f(x0); dfx = df(x0); x = x0 - fx / dfx; if abs(x - x0) < tol break; end x0 = x; end end % 使用方法 f = @(x) log10(x) - 2; df = @(x) 1 / (x * log(10)); bisection(f, 1, 10) newton(f, df, 10)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值