noip2017普及组跳房子 (jump.cpp)

这个题目的主要思路就是二分答案找g值最小值

对于每种g值我们都计算一遍是否能够获得k分

在计算时采用dp dp的主要思路为在当前的g值下对于当前块dp[i] 为从起点跳到第i个块最多收益 显然转移方程 记所有能跳到i块的块为q[j] 则dp[i]=max(q[j])+第i块的权值

如果只用普通dp则时间复杂度为 O(n^2)

所以必须要用单调队列来使复杂度降为 O(n)则总复杂度为O(n*log(S总))


```
#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll n,d,k,s[500001],a[500001],q[500001],m[500001],sum;//s[i]为第i块到起点的距离,a[i]为第i块的权值
//q,m为单调队列的两个参数 q存的是块的权值 m存的是块到起点的距离
bool check(ll g);//二分之后求解
int main(){
    cin>>n>>d>>k;
    for(int i=1;i<=n;i++){
        cin>>s[i]>>a[i];
        if(a[i]>0)sum+=a[i];//把所有正数累加
    }
    if(sum<k){//如果所有正数加完都无法获得要求分数则无解
        cout<<-1;
        return 0;
    }
    ll l=0,r=s[n],mid;
    while(l<=r){//进行二分搜索
        mid=(l+r)>>1;
        if(check(mid)){
            r=mid-1;
        }else{
            l=mid+1;
        }
    }
    cout<<l;
    return 0;
}
bool check(ll g){
    long long dp[500001],minn=d-g,maxx=d+g,head=0,tail=-1,cur=0;//minn为最短要求跳多远,maxx为最多能跳多远 head tail为维护单调队列的队头和队尾 cur为当前要加入单调队列的块的位置
    if(minn<=0)minn=1;
    memset(dp,0,sizeof(dp));//初始化
    for(int i=1;i<=n;i++){//dp循环
        for(;s[cur]<=s[i]-minn && cur<i;cur++){//更新单调队列
            while(head<=tail&&q[tail]<dp[cur])tail--;
            if(dp[cur]<=-1000000000)continue;//没有块能跳到当前这个块则为极小数这样后面因为dp要找尽可能大的数,所以不会找到这个极小数就相当于不会从这个块跳到后面的块
            q[++tail]=dp[cur];m[tail]=s[cur];//把这个块加入单调队列
        }
        while((head<=tail)&&(s[i]-m[head]>maxx))head++;//如果前面单调队列中的块已经跳不到现在的块了则出队列
        if(head<=tail)dp[i]=q[head]+a[i];//找到最大值
        else dp[i]=-1000000000;//找不到说明没有块能跳到这个块
        if(dp[i]>=k)return 1;//如果跳到这个块就已经能达到题目要求分数则说明这个g值是成立的
    }
    return 0;
}
```


猜你喜欢

转载自blog.csdn.net/qq_39591454/article/details/80866414
今日推荐