ACM集训之一:二分法+三分法+二分交互(HYSBZ 1734+POJ 1905+HDU 3714+Gym - 101375H)

版权声明:本文为博主原创文章,转载请注明出自CSDN Authur丶gyc。 https://blog.csdn.net/WHY995987477/article/details/86698740

二分知识点

二分法可应用于
1、从有序数组中快速查找值
2、假定一个解并判断是否可行
3、最大化最小值(以及最小化最大值)
4、最大化平均值

三分知识点

可以应用于:查找一元二次函数的极值

对二分的理解

1、从有序数组中快速查找值
务必是在有序数组中,通过二分查找不断取中点,然后判断中点的值是大于还是小于所要查找的值,进而确定查找值的所在区间,等于则查找到答案,当然也可能数组中不存在该查找值。
2、假定一个解并判断是否可行
先明确题目要求的解是什么,然后列出在题目限制下,该解可能所在的范围(因为是区间,故必定有序),对这个范围进行二分,取中点,然后根据中点的值推出解为该中点值时,已知量对应的值,与题目中所给的已知量的值进行对比,看是大了还是小了进而确定解所在的区间,然后继续二分,不断重复,提高解的精度。一般放在循环中二分一百遍就好(此时精度一般有10的-30次方),太多次会超时。
3、最大化最小值
看到题目的时候没想到能用二分做。
仍然是用第二点用二分对解进行假定并判断解(最小值)是否已经最大,判断部分往往使用贪心法。
4、最大化平均值
在这里插入图片描述
在这里插入图片描述

对三分的理解

三分法可用于求解一元二次方程的极值问题。函数图像为U字型,我们有一个区间,极值点在区间内,现在要我们找到极值的横坐标。
把这个区间进行三等分,然后看两个等分点对应的函数值,比较函数值的大小,通过比较两个值的大小,我们一定能确定极值点必定在哪个区间。
例如,对于U型的函数的区间[a,b],极值点在区间内,三等分,得两个等分点L,R.如果L对应的函数值比R对应的函数值大,则极值必定在区间[L,b],同理如果小,必定在区间[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

#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;
}

猜你喜欢

转载自blog.csdn.net/WHY995987477/article/details/86698740