11屆國賽python試題 J: 藍跳跳
藍跳跳
【問題描述】
小藍製作了一個機器人,取名為藍跳跳,因為這個機器人走路的時候基本靠跳躍。
藍跳跳可以跳著走,也可以掉頭。藍跳跳每步跳的距離都必須是整數,每步可以跳不超過 k 的長度。由於藍跳跳的平衡性設計得不太好,如果連續兩次都是跳躍,而且兩次跳躍的距離都至少是 p,則藍跳跳會摔倒,這是小藍不願意看到的。
小藍接到一個特別的任務,要在一個長為 L 舞臺上展示藍跳跳。小藍要控制藍跳跳從舞臺的左邊走到右邊,然後掉頭,然後從右邊走到左邊,然後掉頭,然後再從左邊走到右邊,然後掉頭,再從右邊走到左邊,然後掉頭,如此往復。為了讓觀者不至於太無趣,小藍決定讓藍跳跳每次用不同的方式來走。小藍將藍跳跳每一步跳的距離記錄下來,按順序排成一列,顯然這一列數每個都不超過 k 且和是 L。這樣走一趟就會出來一列數。如果兩列數的長度不同,或者兩列數中存在一個位置數值不同,就認為是不同的方案。
【輸入格式】
輸入一行包含三個整數 k, p, L。
【輸出格式】
輸出一個整數,表示答案。答案可能很大,請輸出答案除以 20201114 的餘數。
【樣例輸入】
3 2 5
【樣例輸出】
9
【樣例說明】
藍跳跳有以下 9 種跳法:
1+1+1+1+1
1+1+1+2
1+1+2+1
1+2+1+1
2+1+1+1
2+1+2
1+1+3
1+3+1
3+1+1
【樣例輸入】
5 3 10
【樣例輸出】
397
方法一
思路:
這種題可以用遞迴的DFS解也可以用動態規劃,我們一看就知道遞迴的話是跑不完資料的,所以我用了動態規劃做的此題,我們可以建立一個2維陣列dp[i][j] ,i 表示舞臺距離,j表示第一次跳j格時。
![圖一](https://img.796t.com/res/2021/01-10/13/dcc7cb77773267111b0c098b7c88a71f.png)
我們當知道第一種結果後就可以遞推後面的結果了。i-j是當為0或者負數時的條件,當為0就沒有第2種選擇,所以就本身一種方法。當為負數就沒有往下走的必要了。
如果連續兩次都是跳躍,而且兩次跳躍的距離都至少是 p,我們知道j是第一種跳法,k就是第2個選擇的跳法所以 j>=b 分為二種轉移方程,當j>=b , dp[i][j]+=dp[i-j][k] 否則 dp[i][j]+=sum(dp[i-j])。最終結果就是sum(dp[s])。
但是這種不是最優解最多跑80%的資料。
程式:
#i 總步數
#j 跳一次的步數
p,b,s=map(int,input().split())
o=0
dp=[[0 for i1 in range(p+1) ] for i in range(s+1)]
dp[1][1]=1
for i in range(2,s+1):
for j in range(1,p+1):
if i-j==0:
dp[i][j]+=1
elif j>=b:
for k in range(1,b):
dp[i][j]+=dp[i-j][k]%20201114
else:
dp[i][j]+=sum(dp[i-j])%20201114
print(sum(dp[s])%20201114)
方法二
思路:
建立一個雙層的迴圈陣列,0層存j<b的方式個數,1層存b<p+1方式個數。和上面方法差不多隻不過是以雙層的格式儲存,這樣可以少很多的計算量。
我們先把已知的資料寫入二維資料中dp[1][0]=1,然後遍歷2,s+1和1,p+1,因為當s=0時 方法只有一種。把dp[0][1]=1 或者dp[0][0]=1 我只是把這個當上面方法dp[i][j]+=1用了。
這個轉移方程也是2個,一個 (1,b) 和(b,p+1) 把j<b 存在dp[i][0] b<p+1存在dp[i][1]。
轉移方程分別為 dp[i][0]+=dp[i-j][1]+dp[i-j][0], dp[i][1]+=dp[i-j][0]。
這種就可以把資料跑完了計算量把上個方法少了很多。
p,b,s=map(int,input().split())
dp=[[0 for i1 in range(2) ] for i in range(s+1)]
dp[0][1]=1
dp[1][0]=1
for i in range(2,s+1):
for j in range(1,b):
if i-j<0:
break
dp[i][0]+=((dp[i-j][1]+dp[i-j][0])%20201114)
for j in range(b,p+1):
if i-j<0:
break
dp[i][1]+=dp[i-j][0]%20201114
print(sum(dp[s])%20201114)