• 【NOIP2017】跳房子 题解(单调队列优化线性DP)


    前言:把鸽了1个月的博客补上

    -----------------

    题目链接

    题目大意:机器人的灵敏性为$d$。每次可以花费$g$个金币来改造机器人,那么机器人向右跳的范围为$[min(d-g,1),max(d+g,x[n])]$。每个点都有分数$w[i]$。问至少花费多少金币得到分数$k$?

    首先,如果用$g$个金币能满足条件,那么$g+1$也能。显然我们要最大值最小,所以我们不妨二分$g$,来求得满足条件的$g$的最小值。

    普通的dp应该还是比较好写的。可以拿60pts.

    bool check(int g)
    {
        for (int i=1;i<=n;i++) f[i]=-0x3f3f3f3f;f[0]=0;
        int rpos=d+g,lpos=max(d-g,1);
        for (int i=1;i<=n;i++)
        {
            for (int j=i=1;j>=1;j--)
            {
                if (x[i]-x[j]<lpos) continue;
                if (x[i]-x[j]>rpos) break;
                f[i]=max(f[i],f[j]+w[i]);
                if (f[i]>=k) return 1;
            }
        }
        return 0;
    }

    对于每一个i,都有一定的可取范围(l,r)。注意到l,r都是单调递增的,且dp方程为$f[i]=max(f[j])+w[i]$,我们可以尝试用单调队列优化。时间复杂度$O(n)$。

    bool check(int g)
    {
        memset(q,0,sizeof(q));
        memset(f,0x80,sizeof(f));f[0]=0;
        int l=1,r=0,j=0;
        int L=d-g,R=d+g;
        if (L<0) L=1;
        for (int i=1;i<=n;i++)
        {
            while(x[i]-x[j]>=L&&j<i)
            {
                if (f[j]!=neInf)//f[j]必须是已经更新过的
                {
                    while(l<=r&&f[q[r]]<=f[j]) r--;//把劣于f[j]的决策排除,因为无论如何都不可能选择它们
                    q[++r]=j;//入列
                }
                j++;
            }
            while(l<=r&&x[i]-x[q[l]]>R) l++;//把不满足条件的排除
            if (l<=r) f[i]=f[q[l]]+s[i];//队首一定是最优选择
        }
        int num=neInf;
        for (int i=1;i<=n;i++) num=max(num,f[i]);
        if (num>=k) return 1;
        else return 0;
    }

    代码:

    #include<bits/stdc++.h>
    #define int long long 
    using namespace std;
    const long long neInf=0x8080808080808080;
    int f[500005],n,d,k;
    int q[500005];
    int x[500005],s[500005],sum,aleft,aright,ans;
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
        while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    bool check(int g)
    {
        memset(q,0,sizeof(q));
        memset(f,0x80,sizeof(f));f[0]=0;
        int l=1,r=0,j=0;
        int L=d-g,R=d+g;
        if (L<0) L=1;
        for (int i=1;i<=n;i++)
        {
            while(x[i]-x[j]>=L&&j<i)
            {
                if (f[j]!=neInf)
                {
                    while(l<=r&&f[q[r]]<=f[j]) r--;
                    q[++r]=j;
                }
                j++;
            }
            while(l<=r&&x[i]-x[q[l]]>R) l++;
            if (l<=r) f[i]=f[q[l]]+s[i];
        }
        int num=neInf;
        for (int i=1;i<=n;i++) num=max(num,f[i]);
        if (num>=k) return 1;
        else return 0;
    }
    signed main()
    {
        n=read(),d=read(),k=read();
        for (int i=1;i<=n;i++)
        {
            x[i]=read(),s[i]=read();
            if (s[i]>0) sum+=s[i];
        }
        aright=max(x[n],d);
        if (sum<k){
            cout<<-1;
            return 0;
        }
        while(aleft<=aright)
        {
            int mid=(aleft+aright)>>1;
            if (check(mid)) ans=mid,aright=mid-1;
            else aleft=mid+1;
        }
        cout<<ans;
        return 0;
    }
  • 相关阅读:
    字符串与字典常用命令
    Python学习之路:字符串常用操作
    Python学习之路:购物车实例
    面试题2017
    c#语法学习
    结构化设计模式-桥接模式
    结构型设计模式-适配器模式
    .Net Cache
    设计模式的六大原则
    uml类图关系
  • 原文地址:https://www.cnblogs.com/Invictus-Ocean/p/13188390.html
Copyright © 2020-2023  润新知