题目(浮点二分)
我有N个不同口味、不同大小的派。 有F个朋友会来参加我的派对,每个人会拿到一块派(不能由几个派的小块拼成;可以是一整个派)。 所有人拿到的派必须是同样大小的(但不需要是同样形状的)。 当然,我也要给自己留一块,而这一块也要和其他人的同样大小。 每个派都是一个高为1,半径不等的圆柱体。 请问我们每个人拿到的派最大是多少?
Input
第一行输入一个T代表组数。 随后T组包含两个正整数N和F,1 ≤ N, F ≤ 10000和N个1到10000之间的整数,表示派的数量,朋友的数量和每个派的半径。
Output
输出每个人能得到的最大的派的体积,精确到小数点后四位。
样例
样例输入
3
3 3
4 3 3
1 24
5
10 5
1 4 2 3 4 5 6 5 4 2
样例输出
25.1327
3.1416
50.2655
AC代码
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
double cake[10001];
int T, N, F;
double PI = acos(-1.0); // 可以获得更精确的值
void erfen(double sum)
{
double low, mid, high; // 如果用float,得不出结果 注意:这三个值代表的是半径的平方值(也可以理解为面积),而非半径
/*
设定二分查找的上限,即用所有饼的总面积(先不乘PI,影响精度)除以人数,说明每个人能分到的最大面积也就是这些(其实根本不可能这么大)
另一种设定最大值的方法是取所有饼的最大半径。但我认为不好,因为与其说每个人分到的饼和半径有关,还不如说和半径的平方有关。
*/
high = sum / (F * 1.0);
low = 0;
while (high - low > 1e-8) // 如果单纯的用 if(high>low) 也得不出结果。 因为精度的要求很高
{
mid = (high + low) / 2.0;
int servings = 0;
/*
二分的思想在于此:
依次分析每一张饼,看看以mid*PI为面积时,所有的饼可以分成几份(相同大小)。
如果份数大于人数,则这个mid*PI是可以的,那么再试试可不可以再把单位面积大一点点?(如果不行再慢慢往回缩);
如果小于人数,说明每个人没办法分到那么多,那么再把单位面积mid*PI减小一点看看能不能满足,即high=mid
*/
for (int i = 0; i < N; i++)
{
servings += (int)(cake[i] / mid); // 更易于理解的写法是(cake[i]*PI)/(mid*PI).表示在当前设定的单位面积下,每张饼可以分成几份,即这张饼可以分给几个人
}
if (F > servings)
high = mid; // 当前方案下所有的饼可以分成的等大份数 小于 人数 。说明单位面积需要缩小一下
else
low = mid; //当前方案下所有的饼可以分成的等大份数 大于 人数 。说明单位面积还有进一步增大的空间
}
printf("%.4lf\n", low * PI); // low只代表半径的平方。还要再乘以PI
}
int main()
{
cin >> T;
while (T--)
{
double sum = 0;
cin >> N >> F;
for (int i = 0; i < N; i++)
{
cin >> cake[i];
cake[i] = cake[i] * cake[i]; //因为蛋糕的面积与半径成正比,所以可以直接把平方写在数组里
sum += cake[i];
}
F++;
erfen(sum);
}
}