【POJ3122】Pie(二分)

题面:【POJ3122】Pie

按理来说,这题应该是一道很水的二分题,可是精度问题却折磨了我很久很久,经过无数次的修改,我总算A了这一题。

你可以二分最终每个人所能得到的Pie的大小,而验证函数check()其实也非常简单:

bool check(double x)//验证函数
{
	int t=0;//记录当Pie的大小为x时所能得到的Pie的数量
	for(int i=1;i<=n;i++) t+=floor(s[i]/x);//统计Pie的数量,s[]数组表示每个Pie的大小
	return t>=m;//若所能得到的Pie的数量比人数多(初始化时记得将m加1,因为自己也需要一份,这是一个坑),就返回true,否则为false
}
有了check()函数,就可以轻易地打出二分函数了:
void find(double l,double r)//二分函数
{
	if(abs(l-r)<ff) return;//当l与r之间的差距比ff(1e-6)还小,就退出函数
	double mid=(l+r)/2;//计算l与r的中间值
	if(check(mid)) ans=mid,find(mid+ff,r);//若Pie的大小为mid可以实现,就将mid作为答案,并尝试得到更大的答案
	else find(l,mid-ff);//若Pie的大小为mid无法实现,就向更小的区域去寻找答案
}

这样一来,整个程序就出来了:

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
#define max(x,y) (x>y?x:y)
#define min(x,y) (x<y?x:y)
#define ff 0.000001//一个特别小的数(1e-6)
#define N 10000
using namespace std;
const double pi=4*atan(1.0);//将π的值作为一个常数
int n,m,a[N+5];//n存储Pie的数量,m存储人数,a[]存储每个Pie的半径
double ans,s[N+5];//ans存储答案,s[]存储每个Pie的面积
int read()//读优
{
	int x=0,f=1;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
	if(ch=='-') f=-1,ch=getchar();
	while(ch>='0'&&ch<='9') (x*=10)+=ch-'0',ch=getchar();
	return x*=f;
}
void write(int x)//输优
{
	if(x<0) putchar('-'),x=-x;
	if(x>9) write(x/10);
	putchar(x%10+'0');
}
bool check(double x)//验证函数(详解见上)
{
	int t=0;
	for(int i=1;i<=n;i++) t+=floor(s[i]/x);
	return t>=m;
}
void find(double l,double r)//二分函数(详解见上)
{
	if(abs(l-r)<ff) return;
	double mid=(l+r)/2;
	if(check(mid)) ans=mid,find(mid+ff,r);
	else find(l,mid-ff);
}
int main()
{
	int T=read();//一共有T组数据
	while(T--)
	{
		n=read(),(m=read())++;//将m加1,因为自己也需要一份,这是一个坑
		double Max=0;//存储上界
		for(int i=1;i<=n;i++) a[i]=read(),s[i]=a[i]*a[i]*pi,Max=max(Max,s[i]);//读入Pie的半径,计算出Pie的面积,并更新上界
		ans=0;//答案初始化为0
		find(ff,Max);//进行二分答案
		printf("%.4f\n",ans);//输出(题目要求保留4位小数)
	}
	return 0;
}



版权声明:转载请注明地址


猜你喜欢

转载自blog.csdn.net/chenxiaoran666/article/details/79936950