题目描述
农夫约翰的农场由 $N$ 块田地组成,每块地里都有一定数量的牛,其数量不会少于$1$头,也不会超过$2000$头。
约翰希望用围栏将一部分连续的田地围起来,并使得围起来的区域内每块地包含的牛的数量的平均值达到最大。
围起区域内至少需要包含 $F$ 块地,其中 $F$ 会在输入中给出。
在给定条件下,计算围起区域内每块地包含的牛的数量的平均值可能的最大值是多少。
输入格式
第一行输入整数 $N$ 和 $F$ ,数据间用空格隔开。
接下来 $N$ 行,每行输出一个整数,第$i+1$行输出的整数代表,第i片区域内包含的牛的数目。
输出格式
输出一个整数,表示围起区域内每块地包含的牛的数量的平均值可能的最大值乘以$1000$得到的数值。
数据范围
$1≤N≤100000$
$1≤F≤N$
样例
输入样例:
10 6
6
4
2
10
3
8
5
9
4
1
输出样例:
6500
前缀和+二分
解题思路:首先我们可以这样理解,我们要寻找一段数列,这个数列满足,长度不小于$L$,并且它的子段和非负。也就是我们需要的二分判定。
二分:首先我们的$mid=(l+r)/2$,记住这里不要是>>1,因为这是浮点数除法。然后呢,我们可以进行前缀和运算,$s[i]=s[i-1]+a[i]-mid$,因为首先我们找的mid是这个平均值,其次前缀和,是为了处理子段和,比如说我要算出$[3,5]$的子段和,那么我们只需要输出$s[5]-s[2]$即可。
这里呢,我们要找到这个满足题意的最优解$[l,r]$,那么也就是说$a[l-1]$要尽量地小,然后$a[r]$要尽量地大,所以说我们就需要枚举这个$l$,但是这样的话时间吃不消,那么怎么办呢?我们发现,每一次$r$变大后,$l$的取值范围从$[1,l]$变成了$[1,l+1]$,所以说我们只需要一个变量,存储当前的最小值即可。具体可看代码实现。
C++ 代码
带check版本
#include <bits/stdc++.h>
using namespace std;
#define fir(i,a,b) for (int i=a;i<=b;i++)
#define eps 1e-5
const int N=100100;
int n,f;
double min_val,ans,l,r,mid,a[N],s[N];
int check(double mid)
{
fir(i,1,n)
s[i]=a[i]-mid,s[i]+=s[i-1];
ans=-1e8,min_val=1e8;
fir(i,f,n)
{
min_val=min(min_val,s[i-f]);
ans=max(ans,s[i]-min_val);
}
return ans<=0?0:1;
}
int main()
{
cin>>n>>f;
fir(i,1,n)
cin>>a[i];
l=-1e6,r=1e6;
while(r-l>eps)
{
mid=(l+r)/2;
if (check(mid))
l=mid;
else
r=mid;
}
cout<<(long long)(r*1000);
}
#include <bits/stdc++.h>
using namespace std;
#define eps 1e-5
const int N=100100;
int n,i,j,f;
double a[N],s[N],mid,ans,min_val,l,r;
int main()
{
ios::sync_with_stdio(false);
cin>>n>>f;
for (i=1;i<=n;i++)
cin>>a[i];
l=-1e6,r=1e6;
while(r-l>eps)
{
mid=(l+r)/2;
for (i=1;i<=n;i++)
s[i]=s[i-1]+a[i]-mid;
min_val=1e10;
ans=-1e10;
for (i=f;i<=n;i++)
{
min_val=min(min_val,s[i-f]);
ans=max(ans,s[i]-min_val);
}
if (ans>=0)
l=mid;
else
r=mid;
}
cout<<(int)(r*1000)<<endl;
}
大佬,测试用例中,你的代码单独输出l和mid值为6.5,而输出(int)(l x 1000)和(int)(mid x 1000)值为6499,这是为啥?
同问
同问
因为l是一个比r小的一个浮点数,你先乘以1000再往下取整的话比如l是6.49999乘以1000就是6499.99 用int强制转换往下取整的话 就是6499了 所以最好打印r端点比较好一点
但为什么要打印r呢 感觉按照题目的意思应该是打印mid呀 但是mid出来是6499 不是很懂 请大佬指教一下
大佬大佬为什么l和r要用double类型的呢
l和r代表的含义是 牛数量的平均值
为什么数量是double而不是int呢佬
看题目噢, 求的是平均值, 就可能出现小数, 不能整除的情况
但是是数量啊,不能是1.5头牛吧
别杠
🤡
算出来就是浮点int啥啊
https://www.acwing.com/solution/acwing/content/3115/ O(n), 大佬们点评一下。
这题明明只要O(n)
怎么做啊?大佬说一说吧.
类似于单调队列的做法,我已经A了
还有,无需二分平均值,二分位置即可,我证明过了
大佬可以让代码让我康康吗?学习学习好算法.
求问怎么O(n)
大佬不妨说说,是如何证明的?
https://www.luogu.com.cn/blog/Letusaccepted/solution-p1419
我也是二分的位置,精度太小了,只过了12个点
大佬大佬 为什么输出的结果是r不是mid呢 不是用mid去判断的是否存在么QWQ
首先我们的r的值是mid,这一点要注意.
而且题目要求最大值.
请问这种题的四舍五入怎么算,这题为啥不四舍五入?
这题其实已经四舍五入了.
cout<<(int)(r*1000)<<endl;
其实就是在四舍五入,精度是三位.我开始写的printf(%0.lf)有一个点四舍五入错了
你的代码改成printf(%0.lf)也错了
三位之后就不用入了是吗
看看题目描述吧
强制转int不是直接取整么
为什么用printf就会wa呢
不会吧
请问二分的边界为啥不是1到2000?有什么讲究么?
不要太小即可.没有什么讲究,
有个问题,为什么最终输出的时候是
(r*1000)
而不是(l*1000)
呢?因为精度和二分边界问题(边界其实不明显),所以我们需要是右边界.精度往往会使得l和r有极为细微的差异(如果你写l*1000你会发现样例答案是6499),而且我们这道题目r是最高边界.
soka,关键在
...可能的最大值
,感谢!不客气
请问这个输出有什么讲究么:我的代码输出始终为0,有些摸不着北
有的,因为我们是强制转换成为整数型输出
如果说代码其他部分Debug后,实在找不出问题的话,建议看看输出