這一題很有它的價值,我都要認真思考一番才能夠想到方法。
先講一下題目大意
題目描述
跳房子,也叫跳飛機,是一種世界性的兒童遊戲,也是中國民間傳統的體育遊戲之一。跳房子的遊戲規則如下:
在地面上確定乙個起點,然後在起點右側畫 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 ≥ d時),他的機械人每次可以選擇向右彈跳的距離為 1,2,3,…,d+g-2,d+g-1,d+g。
現在小 r 希望獲得至少 k 分,請問他至少要花多少金幣來改造他的機械人。
輸入
第一行三個正整數 n,d,k,分別表示格仔的數目,改進前機械人彈跳的固定距離,以及希望至少獲得的分數。 相鄰兩個數之間用乙個空格隔開。
接下來 n 行,每行兩個正整數�� �� , �� �� ,分別表示起點到第i個格仔的距離以及第i個格仔的分數。 兩個數之間用乙個空格隔開。 保證�� �� 按遞增順序輸入。
輸出
共一行,乙個整數,表示至少要花多少金幣來改造他的機械人。若無論如何他都無法獲得至少 k 分,輸出-1。
樣例輸入
【輸入樣例 1】
7 4 10
2 6
5 -3
10 3
11 -3
13 1
17 6
20 2
【輸入輸出樣例 2】
7 4 20
2 6
5 -3
10 3
11 -3
13 1
17 6
20 2
樣例輸出
【輸出樣例 1】
2【輸入輸出樣例 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】
-1【輸入輸出樣例 2 說明】
由於樣例中 7 個格仔組合的最大可能數字之和只有 18 ,無論如何都無法獲得 20 分。
資料範圍限制
本題共 10 組測試資料,每組資料 10 分。對於全部的資料滿足1 ≤ n ≤ 500000,1 ≤ d ≤2000,1 ≤ xi,k≤ 10^9 , |s i | < 10^ 5 。
對於第1,2 組測試資料,n ≤ 10;
對於第3,4,5組測試資料, n ≤ 500
對於第 6,7,8 組測試資料,d = 1
這一題有一點難了,不過,這畢竟是noip普及組第四題嘛!
正解——二分+單調佇列。
首先二分答案,然後就可以用乙個單調佇列來做(記得要維持好單調佇列)
時間複雜度就不用說了,相信讀者們都知道(此題可過)。
l:=0;//head
r:=a[n,1]+d;//tail
ans:=-1;//輸出的最小價值
while l<=r do
begin
mid:=(l+r) div
2;//二分變數
if pd(max(1,d-mid),mid+d) then
//pd是用來判斷是否成立的,在下面的單調佇列有講
begin
ans:=mid;
r:=mid-1;
endelse l:=mid+1;
end;
writeln(ans);
單調佇列(判斷能否得到k價值)部分:
function pd(zx,zd:longint):boolean;
var f,q:array[0..500000] of int64;
i,j,head,tail,last:longint;
begin
head:=1;
tail:=1;
last:=-1;//用來優化時間的
q[1]:=0;
f[0]:=0;
for i:=1 to n do
begin
f[i]:=0;
for j:=last+1 to i-1
do begin
if (a[j,1]+zx<=a[i,1]) and (a[j,1]+zd>=a[i,1]) then//判斷能否從a[j,1]跳到a[i,1]
begin
while (f[q[tail]]and (head<=tail) do
dec(tail);//刪尾
inc(tail);
q[tail]:=j;
last:=j;
end;
end;
while ((a[q[head],1]+zd1]) or (a[q[head],1]+zx>a[i,1])) and (head<=tail) do
inc(head);//移頭
if (head>tail) then f[i]:=-maxlongint
else f[i]:=f[q[head]]+a[i,2];//找到從零點到a[i,1]的最大獲得價值
if f[i]>=k then exit(true);
end;
exit(false);
end;
還是上一下源**吧!
var
a:array[0..500000,1..2] of longint;
n,d,k,i,l,r,ans,mid:longint;
function
max(x,y:longint):longint;
begin
if x>y then
exit(x)
else
exit(y);
end;
function
pd(zx,zd:longint):boolean;
var f,q:array[0..500000] of int64;
i,j,head,tail,last:longint;
begin
head:=1;
tail:=1;
last:=-1;
q[1]:=0;
f[0]:=0;
for i:=1
to n do
begin
f[i]:=0;
for j:=last+1
to i-1
dobegin
if (a[j,1]+zx<=a[i,1]) and (a[j,1]+zd>=a[i,1]) then
begin
while (f[q[tail]]and (head<=tail) do
dec(tail);
inc(tail);
q[tail]:=j;
last:=j;
end;
end;
while ((a[q[head],1]+zd1]) or (a[q[head],1]+zx>a[i,1])) and (head<=tail) do
inc(head);
if (head>tail) then f[i]:=-maxlongint
else f[i]:=f[q[head]]+a[i,2];
if f[i]>=k then
exit(true);
end;
exit(false);
end;
begin
assign(input,'jump.in');reset(input);
assign(output,'jump.out');rewrite(output);
readln(n,d,k);
for i:=1
to n do
read(a[i,1],a[i,2]);
l:=0;
r:=a[n,1]+d;
ans:=-1;
while l<=r do
begin
mid:=(l+r) div
2; if pd(max(1,d-mid),mid+d) then
begin
ans:=mid;
r:=mid-1;
endelse l:=mid+1;
end;
writeln(ans);
close(input);
close(output);
end.
這些地方都很容易理解,就是如何實現,因為那些細節真的是太多了!
(今年noip普及組沒能ak,真的是不幸中的不幸)
NOIP 2017 普及組 初賽
noip 2017 普及組 初賽 頭一次,短時間內把同年的 提高組 初賽 普及組 初賽 題目做了一遍。感覺是,普及組的題目相比提高組,真是弱爆了,完全不在乙個層次,題目做下來真的很菜。一 單項選擇題 1.補碼計算原碼,保留首位的符號位不變,剩下的位取反加1.補碼 10101011 補碼取反 1101...
NOIP2017普及組 棋盤
noip2017 有乙個m m的棋盤,棋盤上每乙個格仔可能是紅色 黃色或沒有任何顏色的。你現在 要從棋盤的最左上角走到棋盤的最右下角。任何乙個時刻,你所站在的位置必須是有顏色的 不能是無色的 你只能向上 下 左 右四個方向前進。當你從乙個格仔走向另乙個格仔時,如果兩個格仔的顏色相同,那你 不需要花費...
NOIP2017普及組 棋盤
棋盤 有乙個m m的棋盤,棋盤上每乙個格仔可能是紅色 黃色或沒有任何顏色的。你現在 要從棋盤的最左上角走到棋盤的最右下角。任何乙個時刻,你所站在的位置必須是有顏色的 不能是無色的 你只能向上 下 左 右四個方向前進。當你從乙個格仔走向另乙個格仔時,如果兩個格仔的顏色相同,那你 不需要花費金幣 如果不...