[洛谷P3957] 跳房子
洛谷題目連接:跳房子
題目描述
跳房子,也叫跳飛機,是一種世界性的兒童遊戲,也是中國民間傳統的體育遊戲之一。
跳房子的遊戲規則如下:
在地面上確定一個起點,然後在起點右側畫 \(n\) 個格子,這些格子都在同一條直線上。每個格子內有一個數字(整數),表示到達這個 格子能得到的分數。玩家第一次從起點開始向右跳,跳到起點右側的一個格子內。第二次再從當前位置繼續向右跳,依此類推。規則規定:
玩家每次都必須跳到當前位置右側的一個格子內。玩家可以在任意時刻結束遊戲,獲得的分數為曾經到達過的格子中的數字之和。
現在小 \(R\) 研發了一款彈跳機器人來參加這個遊戲。但是這個機器人有一個非常嚴重的缺陷,它每次向右彈跳的距離只能為固定的 \(d\)
現在小 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] 跳房子