Gameboy 站在座標 5 位置,餡餅從天上掉落在 0-10 共 11 個位置。每秒移動範圍 ≤ 1 格(左/右/不動)。第 T 秒在位置 x 掉落餡餅(可能多個)。背包無限大。求最多能接住多少餡餅。n < 100000, T < 100000。
正向 DP 需處理左右邊界(位置 0 不能再左,10 不能再右)。逆向 DP 從最大時刻 maxT 向 0 遞推:
dp[t][pos] = 從時刻 t 位置 pos 開始,到結束能接的最大餡餅數dp[t][pos] += max(dp[t+1][pos-1], dp[t+1][pos], dp[t+1][pos+1])(邊界自動處理)dp[0][5](初始時刻 0,位置 5)// 內層循環:位置 0 到 10
for (int pos = 0; pos <= 10; pos++) {
if (pos == 0)
dp[t][pos] += max(dp[t+1][0], dp[t+1][1]);
else if (pos == 10)
dp[t][pos] += max(dp[t+1][9], dp[t+1][10]);
else
dp[t][pos] += max({dp[t+1][pos-1], dp[t+1][pos], dp[t+1][pos+1]});
}
只依賴 t+1 時刻,使用兩行滾動:
int dp[2][11]; // 當前行、下一行
int cur = 0, nxt = 1;
for (int t = maxT - 1; t >= 0; t--) {
for (int pos = 0; pos <= 10; pos++) {
// 計算 dp[cur][pos] 基於 dp[nxt][...]
}
swap(cur, nxt);
}
答案 = dp[cur][5]; // 最後一次 swap 後 cur 指向 t=0
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXT = 100000 + 5;
int dp[MAXT][11]; // 靜態數組足夠快,無需滾動
int main() {
int n, x, T;
while (scanf("%d", &n) == 1 && n) {
memset(dp, 0, sizeof(dp));
int maxT = 0;
for (int i = 0; i < n; i++) {
scanf("%d%d", &x, &T);
dp[T][x]++; // 同一時刻同位置可能多個餡餅
maxT = max(maxT, T);
}
// 逆向 DP
for (int t = maxT - 1; t >= 0; t--) {
for (int pos = 0; pos <= 10; pos++) {
if (pos == 0)
dp[t][pos] += max(dp[t+1][0], dp[t+1][1]);
else if (pos == 10)
dp[t][pos] += max(dp[t+1][9], dp[t+1][10]);
else
dp[t][pos] += max({dp[t+1][pos-1], dp[t+1][pos], dp[t+1][pos+1]});
}
}
printf("%d\n", dp[0][5]);
}
return 0;
}
maxT ≤ 100000 直接開數組更快dp[T][x]++ 而非賦值 1cin/cout 會 TLEdp[100001][11] 約 4.4MB,靜態分配無壓力O(maxT × 11) = O(1.1M) 操作,極快O(maxT × 11) 整數,約 4.4MB若位置範圍擴大至 1000,可改用 map/unordered_map 稀疏存儲,或滾動數組 dp[2][1001] 節省空間。逆向 DP 思想通用。