感想:绝对得好题,开始自己想了半天的状态转移方程,感觉也就一个枚举头尾的方法,但是100000啊,这可不是闹着玩的,所以决定去看大牛的代码,看完之后发现这状态转移方程怎么和求连续最大值一样,
f[i] = max((sum[i] - sum[i - f])/f,(f[i -1] + cow[i])/(num[i-1]+1))这个是错的,请勿相信
半信半疑的敲了,A了,但是感觉还是不对劲,去看了看讨论板,别人给的5 3 100 200 1 1 500得出来的答案根本就是错的,所以大家以后看解题报告的时候一定要弄清因果,刚才那种方法根本就是错的。
看见有凸包解法和二分的,凸包的不会,果断决定去看二分的。
二分法的关键在于判断“一个可能的解跟正确答案相比是大了还是小了”。网上给的方法是:
如果要判断val这个解,那就让序列里所有元素的值都减去val。
然后试图寻找一段连续的区间,该区间的长度大于F,并且区间大于0。
可见,问题一下转化成统计数字的和,而不是数字的平均值,问题变得明朗了、刚才那个错的Dp方法在这里刚才派上了用场。
寻找这种区间的算法是一个很简单的动态规划,复杂度为O(N)。
用 f[a, b] 表示在区间 [a, b] 中,所有子区间的最大值。
那么
当 b - a = F 时,dp[a, b] 为序列中对应的和。
当 b - a > F 时,dp[a, b] = max{ dp[a, b - 1] + arr[b], dp[b - f + 1, b] }
l尽量开大,小心wa
#include<cstdio>
#include<cstring>
#include<iostream>
#define N 100010
using namespace std;
int n,f;
double sum[N],arr[N];
bool judge(double x)
{
double cur, pre;
int i;
pre = sum[f - 1] - x* (f - 1);
for (i = f; i <= n; i++) {
cur = sum[i] - sum[i - f] - x * f;
pre = pre + arr[i] - x;
if (cur > pre)
pre = cur;
if (pre > -1e-6)
return true;
}
return false;
}
int main(void)
{
cin>>n>>f;
sum[0] = 0;
double m,r=0,l=1e60;
for(int i=1;i<=n;++i)
{
scanf("%lf",arr+i);
if(arr[i]>r)
r = arr[i];
if(arr[i]<l)
l = arr[i];
sum[i] = sum[i-1] + arr[i];
}
while((r-l)>1e-7)
{
m = (r+l)/2;
if(judge(m))
l = m;
else
r = m;
}
printf("%d\n",(int)(r*1000));
return 0;
}
本文介绍了一种利用二分法解决寻找序列中连续子序列的最大平均值问题的方法。通过对序列进行预处理,使用动态规划技巧快速找到符合条件的子序列,并通过二分查找逐步逼近最优解。

1044

被折叠的 条评论
为什么被折叠?



