大致思路为先自定义left,right的值,然后使用二分法得到mid,测试mid是大于要求的值还是小于要求的值,然后最终使left 与right无限逼近答案。
#include <cstdio>
#include <cmath>
using namespace std;
const double inf = 200005;
double num[10005];
int N,T;
bool pan(double x)
{
int sum = 0;
for(int i = 1;i<= N;i++)
sum += (int)(num[i]/x);
return sum>=T;
}
int main()
{
while(~scanf("%d%d",&N,&T))
{
for(int i = 1;i<=N ;i++)
scanf("%lf",&num[i]);
double ri = inf,le = 0;
while(ri-le>1e-9)//for(int i=1;i<=100;i++)
{
double mid = (ri+le)/2;
if(pan(mid))
le = mid;
else
ri = mid;
}
printf("%.2f\n",floor(ri*100)/100);//此处的for可以使用le
/*题目要求求最大值故用ri ,但当精度非常大时,ri与le是几乎相同
并且答案要求保留小数点后两位,故ri与le 都可以,
在这个中,while与for达到相同精度while所循环次数大于for,会出问题,
所以建议使用for,i<=100足以精确到小数点后30位. */
}
return 0;
}
对于pan函数中为什么是sum>=T,而不是sum>T 我在这里解释一下:
首先 sum的值象征着什么?
从代码中我们可以看到sum是由 (int)(num[i]/x)的结果不断累加产生的,故sum的结果越大,说明x越小,注意这里用到了强制转换。所以sum是将多出的小数毛刺去掉了再累加的结果。
其次 如果这个sum计算出的结果是等于sum的,那我们应该将此时的x放到左,还是右?
尽管是sum是等于 T的,但不要忘记,此时的sum只是去掉小数毛刺后的累加值。
所以说实际的sum(不强转(int)去掉毛刺的情况下)可能是要比T大的。说明x实际上是可能比正确答案要小的。所以我们将其放到左侧 而不是右侧。
添加一行代码来测试:
for(int i=1;i<=100;i++)
{
double mid = (ri+le)/2;
if(pan(mid))
le = mid;
else
ri = mid;
//添加的测试数据代码
printf("行号:%d le = %.2f mid = %.2f ri = %.2f\n",i,floor(le*100)/100,floor(mid*100)/100,floor(ri*100)/100);
}
两次如图:左侧(sum>T) 右侧(sum>=T)
左图的数据是sum等于T的时候 将x放到右侧(sum > T)
右图的数据是sum等于T的时候 将x放到左侧(sum >= T)
我们可以发现17行及其之前的数据是相同的,但是在第18行出现了不同,
左图将0.76放到了右侧(ri = mid)
右图将0.76放到了左侧(le = mid)
实际的sum(不强转(int)去掉小数毛刺的情况下)可能是要比T大的。说明x实际上是可能比正确答案要小的。所以应当le = mid,代码写为 sum >= T。(如右图)