题目链接:
链接: [link](信息学奥赛一本通(C++版)在线评测系统).
首先这道题要求找平均数,且要求所选片段长度需要大于等于l。那么首先想到的就是最暴力的方法就是找到所有大于等于l的片段在其中找到最大的平均数,这显然没理解到出题人的要求。此片文章是看了民间题解后我的理解。
思路:首先这道题很明显有个明确的要求就是找到一个平均值且有明显的分界点。分界点就是若当前所有大于等于l的片段的平均值都小于当前找到的这个平均值那么当前的这个平均值就大了,若当前所有大于等于l的片段存在大于等于当前的这个平均值那么就可以继续寻找下一个更大的平均值。看到这里你一定明白了是很明显的二分思想。
简化:在使用二分的同时如何简化题目要求呢,观察发现题目要求片段长度大于等于l,那么我们可以将片段分割为两个部分,后面一部分长度为l-1因为长度最低要求l此时前面部分至少要选取1个长度的片段,那么这前面部分的长度由最短片段(1)为结尾的最大平均数片段决定。
图示:
再次简化:此时如果数组中每个数减去平均数那么此时的二分的分界条件就成了所选片段是否大于等于0.
#include<bits/stdc++.h>
using namespace std;
int n,L;
//i:存数,p:减去mid之后的数组,j:以c为结尾的和,k:以c为结尾的最大连续区间的和
double ans,i[100005],p[100005],j[100005],k[100005];
int main()
{
scanf("%d%d",&n,&L);
double l=0,r=2000,jin=1e-6,mid;
for(int c=0;c<n;c++) scanf("%lf",&i[c]);
while(r-l>jin){
mid=(l+r)/2;
for(int c=0;c<n;c++){
p[c]=i[c]-mid;
if(c){
k[c]=max(k[c-1]+p[c],p[c]);
j[c]=j[c-1]+p[c];
}else{
j[c]=k[c]=p[c];
}
}
ans=j[L-1];
for(int c=L;c<n;c++){
double ans1=j[c]-j[c-L+1]+k[c-L+1];
ans=max(ans,ans1);
if(ans>=0) break;
}
ans>=0?l=mid:r=mid;
}
printf("%d",(int)(r*1000));
}
代码中jin表示精度题目中的数据表示最多有1e5个数,每个数在0和2000之间,那么所有可能存在的平均数之间的差值为1e-5的倍数,所以当二分的左值与右值之间的差小于1e-5时所有可能的平均值已经找完了。然后对于结果是r还是l还是mid的判断是因为当结束while循环时mid的值不是1e-5那么mid自然不是当然找的平均数,l和r都是之前所找的平均的而题意中要求找最大的平均值,自然r是最后的平均值。最后要主意对于末尾的强制类型转换不是(int)r*1000需要加上括号,否则程序会先转化再乘以1000这样会丢失平均数小数点后的数据。
我是笨蛋