MAX Average Problem
题目:http://acm.hdu.edu.cn/showproblem.php?pid=2993
题意:给一段数字序列, 求长度不小于 K 的平均值最大的子序列。
题解:不难写出dp方程dp[i]=max{(summ[i]-summ[j])/(i-j)}(j<i,i-j>=k)。这个肯定要超时,我们要进行优化。如果我们把(i,summ[i])看成一个点就会发现求最大值其实就是求哪两点连线的斜率最大,这个我们可以通过维护一个下凸曲线和二分查找很快的找到最大值。详细的可以看《浅谈数形结合思想在信息学竞赛中的应用》中的最大平均值问题。
ps:这题还要注意必须用输入挂,不然必超时。还要数据类型最好用long long 或double 。
代码:
#include<cstdio>
#include<cstring>
using namespace std;
#define MAX 100005
#define max(a,b) ((a)>(b)?(a):(b))
int q[MAX];
double summ[MAX];
//dp[i]=max{(summ[i]-summ[j])/(i-j)}(j<i,i-j>=k)
int idx[MAX];
double cross(int a,int b,int c)//比较斜率
{
double x0=idx[b]-idx[a];
double y0=summ[b]-summ[a];
double x1=idx[c]-idx[b];
double y1=summ[c]-summ[b];
return x0*y1-x1*y0;
}
int binarySearch(int l,int r,int id)//二分查找
{
int mid;
for(;l<r;)
{
mid=(l+r)>>1;
if(cross(q[mid],q[mid+1],id)<0)
r=mid;
else
l=mid+1;
}
return l;
}
void scan(double &n)
{
char c;
for(;c=getchar(),c<'0'||c>'9';);
n=c-'0';
for(;c=getchar(),c>='0'&&c<='9';)
n=n*10+c-'0';
}
int main()
{
int n,k;
int back;
double cnt,ans;
for(;~scanf("%d%d",&n,&k);)
{
summ[0]=idx[0]=0;
for(int i=1;i<=n;++i)
{
//scanf("%lf",&cnt);
scan(cnt);
summ[i]=summ[i-1]+cnt;
idx[i]=i;
}
ans=back=0;
for(int i=k;i<=n;++i)
{
for(;back>1&&cross(q[back-1],q[back],i-k)<0;--back);
q[++back]=i-k;
int opt=binarySearch(1,back,i);
cnt=(summ[q[opt]]-summ[i])/(idx[q[opt]]-idx[i]);
ans=max(ans,cnt);
}
printf("%.2f\n",ans);
}
return 0;
}
来源:
http://blog.youkuaiyun.com/acm_ted/article/details/7910362