【DP+斜率优化】 hdu2993 MAX Average Problem

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

你可能感兴趣的:(【DP+斜率优化】 hdu2993 MAX Average Problem)