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.csdn.net/acm_ted/article/details/7910362