zoukankan      html  css  js  c++  java
  • 跳房子

    传送门

    其实这道题只要想清楚
    dp循环变量的意义
    以及dp所求的值
    就非常简单了

    解法:

    (i)点获得的分为(S_i)

    先来考虑一个方面的问题

    若我们知道(g) 即机器人性能的改变值

    怎么求获得的最多的分

    可以想到(dp)

    (dp[i])表示到i点为止可以获得的最大分

    状态转移方程即为 (dp[i]=max_{j=0->i-1}{dp[j]}+S_i ext{条件:}(d-gle X_i-X_jle d+g))

    (从0开始是因为可以从一开始位置就直接跳到(i)点)

    p.s.可以发现其实并不需要枚举j,用单调队列维护即可。

    这样我们就找到了知道(g)的情况下求获得最大分的方法

    接下来就用2分找(g)就可以了

    当然题目未给上界(有是有,但是为(10^9)

    所以我们应先用倍增来求(g)的区间

    代码:

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #define inf 2000000000
    #define max(x,y) ((x)>(y)?(x):(y))
    #define min(x,y) ((x)<(y)?(x):(y))
    #define rep(i,a,b) for(int i=(a);i<=(b);++i)
    #define dwn(i,a,b) for(int i=(a);i>=(b);--i)
    using namespace std;
    typedef long long ll;
    int l,r;
    int n,d,k,x[500010],s[500010];
    ll dp[500010],sum=0;
    int solve(int g)
    {
        memset(dp,-127,sizeof(dp));
        int lp=max(1,d-g),rp=d+g;
        ll ans=-inf;
        dp[0]=0;
        rep(i,1,n)
        {
            dwn(j,i-1,0)
            {
                if(x[i]-x[j]>rp) break;
                if(x[i]-x[j]<lp) continue;
                dp[i]=max(dp[i],dp[j]+s[i]);
            }
            ans=max(ans,dp[i]);
            if(ans>=k) return ans;
        }
        return ans;
    }
    int main()
    {
        scanf("%d%d%d",&n,&d,&k);
        rep(i,1,n)
        {
            scanf("%d%d",&x[i],&s[i]);
            if(s[i]>0)sum+=s[i];
        }
        if(sum<k)
        {
            printf("-1
    ");
            return 0;
        }
        r=10;
        while(true)
        {
            if(solve(r)>=k) break;
            r*=10;
        }
        l=r/10;
        while(l<r)
        {
            int mid=(l+r)>>1;
            if(solve(mid)>=k)
                r=mid;
            else
                l=mid+1;
        }
        printf("%d
    ",l);
        return 0;
    }
    
    

    单调队列优化:

    int q[500010],ql,qr;
    int solve(int g)
    {
        memset(dp,-127,sizeof(dp));
        int lp=max(1,d-g),rp=d+g,cnt=0;
        dp[0]=0;
        ql=1,qr=0;
        rep(i,1,n)
        {
            while(true)
            {
                while(ql<=qr&&cnt<i&&x[i]-x[cnt]>=lp&&dp[cnt]>=dp[q[qr]]) qr--;
                if(cnt<i&&x[i]-x[cnt]>=lp) q[++qr]=cnt++;
                else break;
            }
            while(ql<=qr&&x[i]-x[q[ql]]>rp) ql++;
            if(ql<=qr) dp[i]=dp[q[ql]]+s[i];
            if(dp[i]>=k) return dp[i];
        }
        return -inf;
    }
    

    但是由于这道题枚举时边界的特殊性

    用单调队列优化并不能快多少

    所以当做练练手写写吧

  • 相关阅读:
    go ERROR invalid character '<' looking for beginning of value
    C#实现将网址生成二维码图片
    二、WPF入门教程——Bingding学习
    一、WPF入门教程——创建WPF项目
    C#实现DataTable行列转置
    VBS整蛊代码
    Task.WhenAll和Task.WhenAny
    Task.WaitAll和Task.WaitAny
    CancellationTokenSource
    组合ContinueWith
  • 原文地址:https://www.cnblogs.com/MYsBlogs/p/10933869.html
Copyright © 2011-2022 走看看