【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;
}

猜你喜欢

转载自www.cnblogs.com/Invictus-Ocean/p/13188390.html
今日推荐