1. 程式人生 > >[洛谷P3957] 跳房子

[洛谷P3957] 跳房子

HR new 需要 bits 一次 ref 移動 != 增加

洛谷題目連接:跳房子

題目描述

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

跳房子的遊戲規則如下:

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

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

現在小 \(R\) 研發了一款彈跳機器人來參加這個遊戲。但是這個機器人有一個非常嚴重的缺陷,它每次向右彈跳的距離只能為固定的 \(d\)

。小 \(R\) 希望改進他的機器人,如果他花 \(g\) 個金幣改進他的機器人,那麽他的機器人靈活性就能增加 \(g\) ,但是需要註意的是,每 次彈跳的距離至少為 \(1\) 。具體而言,當 \(g<d\) 時,他的機器人每次可以選擇向右彈跳的距離為 \(d-g,d-g+1,d-g+2\) ,…, \(d+g-2\)\(d+g-1\)\(d+g\) ;否則(當 \(g \geq d\) 時),他的機器人每次可以選擇向右彈跳的距離為 \(1\)\(2\)\(3\) ,…, \(d+g-2\)\(d+g-1\)\(d+g\)

現在小 RRR 希望獲得至少 \(k\)

分,請問他至少要花多少金幣來改造他的機器人。

輸入輸出格式

輸入格式:

第一行三個正整數 \(n\)\(d\)\(k\),分別表示格子的數目,改進前機器人彈跳的固定距離,以及希望至少獲得的分數。相鄰兩個數 之間用一個空格隔開。

接下來 \(n\) 行,每行兩個正整數 \(x_i,s_i\)? ,分別表示起點到第 \(i\) 個格子的距離以及第 \(i\) 個格子的分數。兩個數之間用一個空格隔開。保證 \(x_i\)? 按遞增順序輸入。

輸出格式:

共一行,一個整數,表示至少要花多少金幣來改造他的機器人。若無論如何他都無法獲得至少 \(k\) 分,輸出 ?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^5\)

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

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

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

一句話題意: 不花錢每次可以走\(d\)的距離,一開始在0的位置,到達一個位置就可以獲得這個位置的權值,花\(g\)個金幣就可以走\([max(1, d-g), d+g]\)的距離(必須要走到一個正好有一個點的位置才能走),問至少要花多少個金幣才能在任意位置獲得大於等於\(k\)的權值,如果不能則輸出-1.

題解: 首先看著這個神奇的花費,應該想到的是二分,因為這個移動的距離是與金幣的使用量有關的,金幣使用得越多則可以走到的距離也就越多,事實上這個是具有單調性的,所以二分來枚舉答案.

然後我們來考慮一下如何驗證.顯然直接枚舉可以轉移狀態的所有情況需要\(O(n^2)\)的時間復雜度.所以這考慮優化.因為這個可轉移的狀態也是可以遞推的,也就是說假設有一個狀態先\(x\)無法轉移到狀態\(y\),且狀態\(z\)\(y\)之後,那麽\(x\)就一定無法轉移\(z\)(這裏的狀態指的是第幾個點,也就是第幾個位置).那麽根據這個性質可以用單調隊列來優化.

如果單調隊列不會寫,可以先看看這道題:滑動窗口

那麽以滑動窗口的思想來理解的話,就是把所有\({pos[j]+max(1, d-g) < pos[i] ≤ pos[j]+d+g\)的點都加入單調隊列中,然後取加入這些點之後的最大值來更新\(i\).

最後在判斷更新的時候還要判斷當前隊列中有元素且不為-inf(賦的最小值).

#include<bits/stdc++.h>
using namespace std;
const int N=500000+5;
const int inf=2147483647;

int n, d, k, p[N], w[N], ans = -1, q[N];
int f[N], h, t;

int gi(){
    int ans = 0, f = 1; char i = getchar();
    while(i<'0'||i>'9'){if(i=='-')f=-1;i=getchar();}
    while(i>='0'&&i<='9'){ans=ans*10+i-'0';i=getchar();}
    return ans * f;
}

void push(int x){while(f[q[t]] <= f[x] && h <= t) t--; q[++t] = x;}

bool check(int mid){
    for(int i=1;i<=n;i++) f[i] = -inf; f[0] = 0;
    h = 1, t = 0, q[h] = 0; int s = 0, mx = d+mid, mn = max(1, d-mid);
    for(int i=1;i<=n;i++){
        while(p[s]+mn <= p[i] && s < i) push(s++);
        while(h <= t && p[q[h]]+mx < p[i]) h++;
        if(h <= t && f[q[h]] != -inf) f[i] = f[q[h]]+w[i];
        if(f[i] >= k) return true;
    }
    return false;
}

int main(){
    n = gi(), d = gi(), k = gi();
    for(int i=1;i<=n;i++) p[i] = gi(), w[i] = gi();
    int l = 1, r = 1000000000;
    while(l <= r){
        int mid = (l+r>>1);
        if(check(mid)) r = mid-1, ans = mid;
        else l = mid+1;
    }
    printf("%d\n", ans);
    return 0;
}

[洛谷P3957] 跳房子