题目链接:平均数
考虑两种算法:
1、二分
每次判断能不能满足存在长度大于m的子串的平均值>=mid【可以考虑使用前缀和优化】
这个思路比较简单,代码:
1 #define INF 0x7fffffff 2 #include<cstdio> 3 #include<iostream> 4 #include<algorithm> 5 using namespace std; 6 const int MAXN=100010; 7 long long a[MAXN],sum[MAXN]; 8 int n,m; 9 inline int read() 10 { 11 int x=0,w=1; 12 char c=getchar(); 13 while(c>'9'||c<'0'){if(c=='-') w=-1; c=getchar();} 14 while(c<='9'&&c>='0'){x=(x<<1)+(x<<3)+c-'0';c=getchar();} 15 return w*x; 16 } 17 bool judge(int mid) 18 { 19 long long minn=INF; 20 for(int i=1;i<=n;++i) 21 { 22 sum[i]=sum[i-1]+a[i]-mid; 23 if(i>=m) 24 { 25 minn=min(minn,sum[i-m]); 26 if(sum[i]>=minn) 27 return true; 28 } 29 } 30 return false; 31 } 32 int main() 33 { 34 n=read(),m=read(); 35 for(int i=1;i<=n;++i) 36 a[i]=read(),a[i]*=10000; 37 int l=0,r=20000000; 38 while(l<r) 39 { 40 int mid=(l+r)>>1; 41 if(judge(mid)) l=mid+1; 42 else r=mid-1; 43 } 44 if(!judge(l)) --l; 45 printf("%d ",l/10); 46 return 0; 47 }
2、数学推导
*发现很多题目跑的最快的算法都是别人的,比方说这题有人说是可以O(N)的
先求部分和S(x),然后连续子序列平均值就转化为S-x平面上的斜率:ave(x,y)=(S(y)-s(x-1))/(y-x+1)。考虑x<y<z的三个点如果S(y)是上凸的,则这个点一定没贡献。所以有用的点构成一个下凸的折线
用一个队列维护这个折线,加入新点时(如当前点为i,则新点为i-m),如果与队尾2个点形成上凸,则删除队尾点。如果队首2个点与当前点形成上凸,同理删除队首点。最后每次队首元素都是与点i斜率最大的点,再求最值就行了
代码:
1 #include <iostream> 2 #include <cmath> 3 #define N 100005 4 typedef long long ll; 5 using namespace std; 6 ll n,m,s[N]; 7 double ans=0.0; 8 ll q[N],t,h; // 队列 9 10 double k(ll x,ll y){ // 计算s[x],s[y]的斜率 11 return (s[y]-s[x]+0.0)/(y-x); 12 } 13 int main() { 14 cin>>n>>m; 15 for (ll i=1,x;i<=n;i++){ 16 cin>>x; s[i]=s[i-1]+x; 17 } 18 19 for (ll i=m;i<=n;i++){ 20 while (t-h>=2 && k(i-m,q[t-1])<k(i-m,q[t-2])) t--; // 删除上凸点 21 q[t++]=i-m; // 入队 22 while (t-h>=2 && k(i,q[h])<k(i,q[h+1])) h++; // 移动最大斜率点 23 ans=max(ans,k(i,q[h])); 24 } 25 26 cout<<(ll)floor(ans*1000)<<endl; 27 return 0; 28 }