經典動態規劃題目:青蛙從獨木橋起點 (0) 跳到終點 (L),每跳距離在 [S, T] 之間,橋上有 M 顆石子。求青蛙過河最少踩到的石子數。
dp[i] 表示到達位置 i 的最少踩石子數dp[i] = min(dp[i-j]) + stone[i],其中 j ∈ [S, T],i-j ≥ 0stone[i] = 1 若位置 i 有石子,否則 0dp[0] = 0,其餘初始化為大值 (INF)min(dp[L], dp[L+1], ..., dp[L+T-1])(跳過終點亦視為過河)由於 T ≤ 10,轉移只依賴前 T 個狀態,可用大小 T 的循環陣列滾動更新,空間從 O(L) 降為 O(T)。但鑑於 L ≤ 20000,直接 O(L) 空間完全足夠,代碼更清晰。
#include <stdio.h>
#include <string.h>
#define MAXL 20010
#define INF 0x3f3f3f3f
int stone[MAXL];
int dp[MAXL];
int main() {
int L;
while (scanf("%d", &L) == 1 && L) {
int S, T, M;
scanf("%d %d %d", &S, &T, &M);
memset(stone, 0, sizeof(stone));
for (int i = 0; i < M; ++i) {
int pos; scanf("%d", &pos);
stone[pos] = 1;
}
for (int i = 0; i <= L + T; ++i) dp[i] = INF;
dp[0] = 0;
for (int i = S; i <= L + T; ++i) {
for (int j = S; j <= T; ++j) {
if (i - j >= 0) {
if (dp[i-j] + stone[i] < dp[i])
dp[i] = dp[i-j] + stone[i];
}
}
}
int ans = INF;
for (int i = L; i <= L + T; ++i)
if (dp[i] < ans) ans = dp[i];
printf("%d\n", ans);
}
return 0;
}
L+T:青蛙可能從 L-S 直接跳過終點,最遠落在 L+T-1,故 DP 陣列需覆蓋至 L+T0x3f3f3f3f 避免加法溢位,且 memset 可快速填充while(scanf("%d",&L)&&L) 同時處理讀取與終止判斷T 較大(如 1000),可用單調隊列優化轉移至 O(L)pre[i] 記錄前驅位置即可回溯此題是「狀態轉移只依賴固定寬度歷史窗口」型 DP 的典型範例,掌握其建模範式,可舉一反三解決類似跳躍、爬樓梯、最小代價路徑等問題。