如题,这题是一个最优化的问题,暴力肯定会超时,就是上来用前缀和的话,因为地的数量大于等于F,所以最坏时间复杂度为o(N*(N-1))这个算法的话肯定也会超时,因为o(n^2)算法处理的数据只为5000,所以我们可以二分答案,就是最坏的时间复杂度是o(n*logn)能处理的数据为10^6,足够应对该题了。那么需要二分1到2000这个区间,这个问题变成了一个判定问题,判定问题其实比较好解,比mid=256,我们可以去判断原序列中存不存在一个区间的平均值大于mid,如果存在的话说明他的平均值可能有更大的,所以可以二分一下,去掉前半块区间。所以关键就在如何判定原序列中存不存在一个区间的平均值大于mid,这里有一个技巧,我们可以让原序列全都减去mid(avg),那么这个问题就变成了判定原序列中存不存在一个长度大于F的区间的平均值大于0,这里可以用到我们之前说的前缀和技巧,这里可以新加两个指针i和j指向这个区间,判断存不存在一个sum[j]-sum[i]>=0的一个区间(j-i>=F) ,那么我们又可以转换成这样,这里我们可以再加一个minv来记录i前面的区间中的最小值,如果这个最小值比sum[j]还小的话说明,sum[j]-sum[i]这个区间是大于0的。由此我们可以把这个问题求解出来。但是这个问题的时间复杂度是多少?,减去mid的时间复杂度是o(n),扫描再判断的时间复杂度是o(n),加上二分里面判断时间复杂度是o(n*logL),足够应对这题的数据范围。这题的思路是:1.先把最优化问题变成一个判定问题2.把每一个数都减去mid后,存不存在一个长度大于F的连续子序列它的总和非负,把问题变成一个非负性判断。3.用一个双指针的做法,第一个指针i在前,j在后,中间隔了F个距离,用一个变量记录i前面的最小值,最后判断sum[j]是不是大于前面的最小值就OK了。
下面是代码:
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
const int N=100010;
int cow[N];
int n,f;
double sum[N];
bool check(double avg)
{
for(int i=1;i<=n;i++)sum[i]=sum[i-1]+cow[i]-avg;
double minv=0;
for(int i=0,j=f;j<=n;i++,j++)
{
minv=min(minv,sum[i]);
if(sum[j]-minv>=0)return true;
}
return false;
}
int main()
{
cin>>n>>f;
for(int i=1;i<=n;i++)cin>>cow[i];
double l=0,r=2000;
while(r-l>1e-5)//二分
{
double mid=(l+r)/2;
if(check(mid))l=mid;
else r=mid;
}
printf("%d\n",int(r*1000));
return 0;
}
附上二分查找经典算法
二分答案模板
//整数(往下取整)
int search1(int l,int r)
{
while(left<right)
{
int mid=left+right+1>>1;
if(check(mid))left=mid;//check(mid)若真即为答案在右半部
else right=mid-1;
}
return l;
}
//双精度浮点型(往下取整)
int search2(int l,int r)
{
while(r-l<1e-6)
{
double mid=(l+r)/2;
if(check(mid))l=mid;
else r=mid-1;
}
return r;
}