离开了老久,终于又回机房了,有点小激动,发誓要好好学习了,相信在将近四个月的平静后我不会再像之前那么水了,花了一个小时写了这道题,而且是看题解的,没事慢慢来不着急。
题面:
给出长度为n的序列,求长度不小于k的连续子序列,且这个子序列的平均值为最大。
输出这个最大平均值(保留六位小数)
题解:
1)低配版 (n,k<=5000) 枚举左右端点找最大值
2)高配版(n,k<=1e5, a[ i ] < 5000) 二分
对于每个二分值mid进行check,如果存在更优解,则:
那我们把每个a[i]减去一个mid,所以检查时建立个aa[i]=a[i]-mid,b[i]=b[i-1]+aa[i],
问题就转化为:是否存在一个长度满足要求的区间和不等于0。
可以建立一个f数组实现,f[i]表示以i结尾的连续子序列的最大平均值,从左往右扫一遍的过程中更新 f[i] 和 ans,如果ans大于0,则存在更优解。
#include <bits/stdc++.h> using namespace std; inline double read() { char ch=getchar(); int s=0,f=0; while(!(ch<='9'&&ch>='0')) {f|=ch=='-';ch=getchar();} while(ch<='9'&&ch>='0') {s=(s<<3)+(s<<1)+ch-'0';ch=getchar();} return f?-s:s; } int n, k; double l=1e9, r=-1e9, ans, mid; double f[10004],aa[10004],b[10004],a[10004]; bool check(double x) { ans = -1e9; for(int i=1;i<=n;i++) aa[i] = a[i]-x, b[i] = b[i-1]+aa[i]; memset(f,-10,sizeof(f)); for(int i=k;i<=n;i++)//枚举右端点,f[i]表示以i结尾的连续子序列的最大平均值 f[i] = max(f[i-1]+aa[i],b[i]-b[i-k]),ans = max(ans,f[i]); if(ans>=0) return true; else return false; } int main() { n = read(); k = read(); for(int i=1;i<=n;i++) a[i] = read(), l = min(l,a[i]), r = max(r,a[i]); //找到答案的范围l,r while(fabs(r-l)>1e-10) { mid = (r+l)/2; if(check(mid)) l = mid; else r = mid; } printf("%.6lf ",l); return 0; }