跳房子

传送门

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

解法:

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

先来考虑一个方面的问题

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

怎么求获得的最多的分

可以想到\(dp\)

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

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

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

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

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

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

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

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

代码:

#include
#include
#include
#include
#include
#include
#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]=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) break;
        r*=10;
    }
    l=r/10;
    while(l>1;
        if(solve(mid)>=k)
            r=mid;
        else
            l=mid+1;
    }
    printf("%d\n",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=lp&&dp[cnt]>=dp[q[qr]]) qr--;
            if(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;
}

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

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

所以当做练练手写写吧

转载于:https://www.cnblogs.com/MYsBlogs/p/10933869.html

你可能感兴趣的:(跳房子)