zoukankan      html  css  js  c++  java
  • hdu 3276

    Problem from:http://acm.hdu.edu.cn/showproblem.php?pid=3276

    在一个数列中寻找两个不相交且不相邻长度为Len(x<=Len<=y)的连续子序列,使这两个子数列的平均数最大,求该最大平均数

    最大平均数用二分搜索来求

    每次二分搜索一个值k,判断是否存在这样的两个子序列的平均值大于等于k,存在则向上二分搜索,不存在则向下搜索

    如何判断一个数列的平均值与k的大小关系:把每个数减去k,再求所有数的和(此处的和假设用Ek表示)

    DP在该处的运用:DP[i]用来存储数列前 i 个数中,所有 长度为Len(x<=Len<=y)的连续子序列 中 最大的Ek(Ek:上一行有定义)

    剩下的我们就只要枚举原数列下标i(x<i,i+x<=n)

    判断    第1..到..第i-1的数列中 满足长度子序列 最大的Ek    与    第i+1..到..第n的数列中 满足长度子序列 最大的Ek    的和是否>=0

    最后剩下的就是如何实现DP了:

    DP前,先用一个数组sum[],sum[i]存前 i 个数的 Ek

    假设DP[i-1]已实现,则DP[i]=max(DP[i-1]  ,  max(sum[i]-sum[j] (x<= i-j <=y) )  )

    这时用的技巧就是用双端队列快速求 max(sum[i]-sum[j] (x<= i-j <=y) )//i-y<= j <=i-x

    每次求DP[i]时,向双端队列末端加入sum[]下标i-x

    在 向双端队列末端加入sum[]下标i-x 前,判断双端队列末端的元素s,sum[s]是否>sum[i-x],是则弹出该s,直到队列为空或者不存在sum[s]>s[i-x] 再加入i-x

    然后判断双端队列首端的元素s,s是否<i-y,是则弹出该s,直到队列中不存在<i-y的s

    这样的操作可以保证双端队列中的元素s全部满足(i-y<=s<=i-x)且 从首端到末端 的sum[s]是呈递增的

    此时就可以判定DP[i]=max(DP[i-1],sum[i]-sum[s](s为双端队列首端的元素))

    #include<cstdio>
    #include<queue>
    #include<algorithm>
    using namespace std;
    const int MAXN=50010;
    int a[MAXN],n,x,y,Case;
    double sum[MAXN],Ldp[MAXN],Rdp[MAXN];//sum[i]存前i个数的Ek;Ldp[i]存前i个数中 满足长度的子序列 中的最大Ek;Rdp[i]存第i个数到第n个个数中 满足长度的子序列 中的最大Ek
    bool f(double dt){
        sum[0]=0;
        for(int i=1;i<=n;i++)sum[i]=sum[i-1]+a[i]-dt;
        deque<int> que;
        Ldp[x-1]=-1e10;
        for(int i=x;i<n-x;i++){//求Ldp[]
            while(!que.empty() && sum[i-x]<sum[que.back()])
                que.pop_back();
            que.push_back(i-x);
            while(que.front()<i-y)
                que.pop_front();
            Ldp[i]=max(Ldp[i-1],sum[i]-sum[que.front()]);
        }
        que.clear();
        sum[n+1]=0;
        for(int i=n;i>0;i--)sum[i]=sum[i+1]+a[i]-dt;
        Rdp[n-x+2]=-1e10;
        for(int i=n-x+1;i>x;i--){//求Rdp[],原理和求Ldp[]一样
            while(!que.empty() && sum[i+x]<sum[que.back()])
                que.pop_back();
            que.push_back(i+x);
            while(que.front()>i+y)
                que.pop_front();
            Rdp[i]=max(Rdp[i+1],sum[i]-sum[que.front()]);
        }
        for(int i=x+1;i<=n-x;i++)//枚举判断
            if(Ldp[i-1]+Rdp[i+1]>=0)return true;
        return false;
    }
    void work(){
        double L=1,R=200000,ans;
        while(R-L>1e-5){
            ans=(L+R)/2;//二分搜索最大平均值
            if(f(ans))L=ans;
            else R=ans;
        }
        printf("Case %d: %.3lf
    ",++Case,ans);
    }
    int main()
    {
        while(scanf("%d%d%d",&n,&x,&y)!=EOF){
            for(int i=1;i<=n;i++)scanf("%d",&a[i]);
            work();
        }
        return 0;
    }
    View Code

    后来,我用优先队列(编码会简单些)来实现,时间复杂度还是高了点,超时。

    双端队列和队列一样是可以用数组来实现的,尤其是像这样的每个元素最多入队一次出队一次的题,实现起来没压力

  • 相关阅读:
    Codevs 1038 一元三次方程求解 NOIP 2001(导数 牛顿迭代)
    Bzoj 3942: [Usaco2015 Feb]Censoring(kmp)
    Bzoj 1355: [Baltic2009]Radio Transmission(kmp)
    Bzoj 2242: [SDOI2011]计算器(BSGS)
    Cogs 1345. [ZJOI2013] K大数查询(树套树)
    Cogs 58. 延绵的山峰(st表)
    洛谷 P2251 质量检测(st表)
    洛谷 P3382 【模板】三分法(三分 二分)
    Hihocoder #1142 : 三分·三分求极值
    P1967 货车运输
  • 原文地址:https://www.cnblogs.com/cshhr/p/3540957.html
Copyright © 2011-2022 走看看