NOIP2017 跳房子题解

题目链接

题目描述:

太长了不贴了。

解题思路:

不难想到二分g,判断一下,朴素的算法是n^2 * log(n)的。

check()函数:

O(n^2)做法:

f[i]表示到第i个点的最大分数。

f[i]=max(f[j])+s[i] (J满足x[j]+Max>=x[i],x[j]+Min<=x[i])

O(n)的做法:

单调队列优化,

1.用一个指针now记录入栈的结点,当x[i]-x[now]>=Min进入循环,

在循环的过程中去除无法到达的now,把合法的结点加入栈。

2.将满足上述条件的情况下,去除x[pos[l]]+Max<x[i]的情况,

如果l>r表示没有解。赋值为-INF,反之更新。

代码:

#include<bits/stdc++.h>
#define ll long long 
#define R register
using namespace std;
const int N=5e5+5;
int n,d,k,s[N],x[N],ans=0x3f3f3f3f,f[N];
int pos[N];
inline int check(R int g)//二分g
{
    //f[i] 表示 到第i个点 得到分数的最大值
    //f[i]=max(f[i],f[j]+s[i]);j满足   x[j]+Max>=x[i]
    //                            x[j]+Min<=x[i]
    memset(pos,0,sizeof(pos));
    memset(f,0,sizeof(f));
    R int Min=max(1,d-g);
    R int Max=d+g;
    R int l=1,r=0,now=0;
    if(Max<x[1])return 1;
    for(R int i=1;i<=n;i++)
    {
            while(x[i]-x[now]>=Min)
            {
                if(f[now]<=-0x3f3f3f3f)
                {
                    now++;
                    continue;
                }
                while(f[now]>=f[pos[r]]&&l<=r)r--;
                pos[++r]=now;
                now++;
            }
            while(x[pos[l]]+Max<x[i]&&l<=r)l++;
            if(l<=r)
            f[i]=f[pos[l]]+s[i];
            else
            f[i]=-0x3f3f3f3f;
            //printf("%d %d
",i,f[i]);
            if(f[i]>=k)return 0;
    }
    return 1;
}
int main(){
    R int l=0,r=0;
    scanf("%d%d%d",&n,&d,&k);
    for(R int i=1;i<=n;i++)
    scanf("%d%d",&x[i],&s[i]);
    r=x[n];
    while(l<r)
    {
        R int mid=(l+r)/2;
        if(check(mid))
        {
            l=mid+1;
        }
        else
        {
            r=mid;
            ans=min(ans,mid);
        }
    }
    if(ans==0x3f3f3f3f)
    printf("-1");
    else
    printf("%d",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/sky-zxz/p/9883855.html