惯例发题……
考场经验与分析
比赛就A了……
这道题符合二分规则(答案具有单调性)。
所以很容易想到二分答案。
正解
首先,这里涉及到一个实数域的二分(就是小数二分)。
这种二分本质上和二分答案是一样的,但是如果直接打会炸,因为小数可以无限二分(1/2,1/4,1/8,1/16,1/321/2,1/4,1/8,1/16,1/321/2,1/4,1/8,1/16,1/32……子子孙孙无穷匮也)。
所以要加一个精度dltdltdlt。
一般这个dltdltdlt都是实际精度*0.1。
为什么,我们再往下看代码片段
while (fabs(r-l)>dlt)//fabs就是绝对值
{
mid=(l+r)/2;
……
}
所以说其实就是判断两数是否达到精度极限。
但是因为是判断大于,所以只能加高精度。
这样,二分问题就解决了。
还有这个数组会炸,因为只告诉我们hn<=105hn<=10^5hn<=105,我们10510^5105 ∗*∗ 10510^5105会炸,
所以先读入hnhnhn,然后在主程序里inta[n+1][h+1]int a[n+1][h+1]inta[n+1][h+1](机智如我)。
那么只剩下checkcheckcheck(判断)的问题了。
我们可以使用一个O(hn)O(hn)O(hn)的方法
首先设MAXMAXMAX为当前列与二分答案之差的最大值,ttt为当前列与二分答案之差之和,sumsumsum为每列与二分答案之差的最大值之和(MAXMAXMAX之和)=0=0=0
接着枚举i=1……ni=1……ni=1……n
{
将MAX==−mid=-mid=−mid(−mid-mid−mid就是最小值),t=0t=0t=0
然后再枚举j=1……hj=1……hj=1……h
{
ttt加上差,MAXMAXMAX更新
}
sumsumsum加上MAXMAXMAX
}
如果sum>=0sum>=0sum>=0那就合法
这里的意思大概就是寻找每列最优解(最大),然后加起来,如果是大于等于二分答案,说明真正答案大于等于当前答案,故合法。
代码:
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
const double dlt=0.00001;
int h,n;
double l=0,r,mid;
int main()
{
scanf("%d%d",&n,&h);
int a[n+1][h+1];
for (int i=1;i<=n;i++)
for (int j=1;j<=h;j++) scanf("%d",&a[i][j]),r=r>a[i][j]?r:a[i][j];
while (fabs(r-l)>dlt)
{
mid=(l+r)/2.0;
double MAX,t,sum=0;
for(int i=1;i<=n;i++)
{
MAX=-mid,t=0;
for(int j=1;j<=h;j++)
{
t+=a[i][j]-mid;
MAX=max(MAX,t);
}
sum+=MAX;
}
if (sum>=0) l=mid;
else r=mid;
}
printf("%.4lf",l);
}```
有问题@我。