[NOIP2017普及組]跳房子(二分,單調隊列優化dp)

[NOIP2017普及組]跳房子

題目描述

跳房子,也叫跳飛機,是一種世界性的兒童遊戲,也是中國民間傳統的體育遊戲之一。c++

跳房子的遊戲規則以下:測試

在地面上肯定一個起點,而後在起點右側畫 nn 個格子,這些格子都在同一條直線上。每一個格子內有一個數字(整數),表示到達這個 格子能獲得的分數。玩家第一次從起點開始向右跳,跳到起點右側的一個格子內。第二次再從當前位置繼續向右跳,依此類推。規則規定:優化

玩家每次都必須跳到當前位置右側的一個格子內。玩家能夠在任意時刻結束遊戲,得到的分數爲曾經到達過的格子中的數字之和。spa

如今小 RR 研發了一款彈跳機器人來參加這個遊戲。可是這個機器人有一個很是嚴重的缺陷,它每次向右彈跳的距離只能爲固定的 dd 。小 RR 但願改進他的機器人,若是他花 gg 個金幣改進他的機器人,那麼他的機器人靈活性就能增長 gg ,可是須要注意的是,每 次彈跳的距離至少爲 11 。具體而言,當 g<dg<d 時,他的機器人每次能夠選擇向右彈跳的距離爲 d-g,d-g+1,d-g+2d−g,d−g+1,d−g+2 ,…, d+g-2d+g−2 , d+g-1d+g−1 , d+gd+g ;不然(當 g \geq dg≥d 時),他的機器人每次能夠選擇向右彈跳的距離爲 11 , 22 , 33 ,…, d+g-2d+g−2 , d+g-1d+g−1 , d+gd+g 。code

如今小 RR 但願得到至少 kk 分,請問他至少要花多少金幣來改造他的機器人。隊列

輸入輸出格式

輸入格式:遊戲

第一行三個正整數 nn , dd , kk ,分別表示格子的數目,改進前機器人彈跳的固定距離,以及但願至少得到的分數。相鄰兩個數 之間用一個空格隔開。ip

接下來 nn 行,每行兩個正整數 x_i,s_ixi​,si​ ,分別表示起點到第 ii 個格子的距離以及第 ii 個格子的分數。兩個數之間用一個空格隔開。保證 x_ixi​ 按遞增順序輸入。get

輸出格式:it

共一行,一個整數,表示至少要花多少金幣來改造他的機器人。若不管如何他都沒法得到至少 kk 分,輸出 -1−1 。

輸入輸出樣例

輸入樣例#1:

7 4 10
2 6
5 -3
10 3
11 -3
13 1
17 6
20 2

輸出樣例#1:

2

輸入樣例#2:

7 4 20
2 6
5 -3
10 3
11 -3
13 1
17 6
20 2

輸出樣例#2:

-1

說明

【輸入輸出樣例 1 說明】

花費 2 個金幣改進後, 小 R 的機器人依次選擇的向右彈跳的距離分別爲 2, 3, 5, 3, 4,3, 前後到達的位置分別爲 2, 5, 10, 13, 17, 20, 對應 1, 2, 3, 5, 6, 7 這 6 個格子。這些格子中的數字之和 15 即爲小 R 得到的分數。

輸入輸出樣例 2 說明

因爲樣例中 7 個格子組合的最大可能數字之和只有 18 ,不管如何都沒法得到 20 分

數據規模與約定

本題共 10 組測試數據,每組數據 10 分。

對於所有的數據知足 1 ≤ n ≤ 500000, 1 ≤ d ≤2000, 1 ≤ x_i, k ≤ 10^9, |si| < 10^51≤n≤500000,1≤d≤2000,1≤x_i,k≤109,∣si∣<105 。

對於第 1, 2 組測試數據, n ≤ 10;

對於第 3, 4, 5 組測試數據, n ≤ 500

對於第 6, 7, 8 組測試數據, d = 1

二分+dp+單調隊列優化

二分應該很顯然了,花的錢越多跳到範圍越廣,更容易知足條件。
因而咱們二分花多少錢,檢驗可否拿到k分。
dp[i]表示跳到第i個格子的最高得分,轉移方程也很好寫
dp[i]=max(dp[j])+c[i] (mi<=x[i]-x[j]<=ma)
那麼咱們怎麼用單調隊列優化呢?

可是咱們發現了一個問題,對於某個決策k,它可能不能更新i,可是它能更新j(j>i)。這樣就不能直接用根據大小或者沒法更新當前決策把k出隊。

換句話說:不能像普通的單調隊列優化同樣僅憑這個決策的值小於令一個決策的值就將決策排除候選集合,由於咱們的可行決策區間,是從\([i-(d+p),i-(d-p)]\),一個決策的值很大,可是他不必定能夠更新他的下一個格子,一些較劣決策仍然可能成爲最優決策,這是咱們最須要注意的。

因此咱們須要在單調隊列team1的基礎上新建一個候選集合team2,對於一個狀態i,咱們要考慮將候選集合能夠更新i的決策放入單調隊列,而後再更新dp[i]。
還有一些初始值的細節須要注意一下。

具體見代碼:

#include<bits/stdc++.h>
using namespace std;
int read()
{
    int x=0,w=1;char ch=getchar();
    while(ch>'9'||ch<'0'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return x*w;
}
const int N=500010;
int n,d,k;
int x[N],a[N],team1[N],team2[N],dp[N];
bool check(int c)
{
    memset(dp,0,sizeof(dp));
    memset(team1,0,sizeof(team1));
    memset(team2,0,sizeof(team2));
    int mi=max(d-c,1),ma=d+c;
    int l1=1,r1=1,l2=1,r2=0;
    if(x[1]<mi) l1=2,r2=1;
    for(int i=1;i<=n;i++)
    {
        while(l1<=r1&&x[team1[l1]]+ma<x[i]) l1++;
        if(l1<=r1)
        {
            dp[i]=dp[team1[l1]]+a[i];if(dp[i]>=k)return 1;
            team2[++r2]=i;
        }
        while(l2<=r2&&x[i+1]-x[team2[l2]]>=mi)
        {
            while(l1<=r1&&dp[team1[r1]]<dp[team2[l2]])r1--;
            team1[++r1]=team2[l2];
            l2++;
        }
    }return 0;
}
int main()
{
    n=read();d=read();k=read();
    for(int i=1;i<=n;i++) x[i]=read(),a[i]=read();
    int l=0,r=x[n]+1,mid;
    //cout<<"start check"<<endl;
    //cout<<check(3);
    while(l<r)
    {
        mid=(l+r)/2;
        if(check(mid)) r=mid;
        else l=mid+1;
        //cout<<l<<" "<<r<<endl;
    }
    if(l==x[n]+1)cout<<-1<<endl;
    else cout<<l<<endl;
}
相關文章
相關標籤/搜索