最直觉的想法:用 dp[i][dir][x] 表示长度为 i、以 x 结尾、下一步方向为 dir 的序列数。
dir = 0 → 下一步需要下降(刚刚上升过)
dir = 1 → 下一步需要上升(刚刚下降过)
初始化长度为 2 的所有情况(相邻不等即可),然后三重循环向后转移:
for (int x = 0; x < range; x++) { for (int y = 0; y < range; y++) { if (x != y) { if (y > x) dp[2][0][y]++; // 上升→下一步下降 else dp[2][1][y]++; // 下降→下一步上升 } } } for (int i = 2; i < n; i++) { for (int y = 0; y < range; y++) { for (int x = 0; x < range; x++) { if (y < x) dp[i+1][1][y] += dp[i][0][x]; } } for (int y = 0; y < range; y++) { for (int x = 0; x < range; x++) { if (y > x) dp[i+1][0][y] += dp[i][1][x]; } } }
问题:O(n * range²) 超时,三维 long long 数组超内存。
第二版:前缀和/后缀和优化(通过但不够好)
注意到转移本质是求前缀和和后缀和:
dp[i+1][0][y] = sum(dp[i][1][x] for x < y) → 用 前缀和
dp[i+1][1][y] = sum(dp[i][0][x] for x > y) → 用 后缀和
for (int i = 2; i < n; ++i) { // 前缀和 vector<int> pre(range, 0); longlong sum = 0; for (int v = 0; v < range; ++v) { sum = (sum + dp[i][1][v]) % MOD; pre[v] = (int)sum; } // 后缀和 vector<int> suf(range, 0); sum = 0; for (int v = range - 1; v >= 0; --v) { sum = (sum + dp[i][0][v]) % MOD; suf[v] = (int)sum; } // 转移 for (int y = 0; y < range; ++y) { if (y > 0) dp[i+1][0][y] = pre[y-1]; if (y < range-1) dp[i+1][1][y] = suf[y+1]; } }
复杂度降到 O(n * range),但三维 int 数组仍占内存。
第三版:滚动数组 + 单变量前缀和
核心优化:
滚动数组:只保留 cur 和 nxt 两层,省掉 n 维度
单变量滚动:不需要 pre/suf 数组,一边遍历一边累加
classSolution { public: constint MOD = 1e9 + 7; intzigZagArrays(int n, int l, int r){ int range = r - l + 1; vector<vector<int>> cur(2, vector<int>(range, 0));
// 初始化长度为 2 for (int x = 0; x < range; ++x) { for (int y = 0; y < range; ++y) { if (x != y) { if (y > x) cur[0][y]++; else cur[1][y]++; } } }
for (int i = 2; i < n; ++i) { vector<vector<int>> nxt(2, vector<int>(range, 0));
// 前缀和方向:左→右 longlong preSum = 0; for (int y = 0; y < range; ++y) { nxt[0][y] = (int)preSum; preSum = (preSum + cur[1][y]) % MOD; }
// 后缀和方向:右→左 longlong sufSum = 0; for (int y = range - 1; y >= 0; --y) { nxt[1][y] = (int)sufSum; sufSum = (sufSum + cur[0][y]) % MOD; }
cur.swap(nxt); }
longlong ans = 0; for (int v = 0; v < range; ++v) { ans = (ans + cur[0][v] + cur[1][v]) % MOD; } return (int)ans; } };
classSolution { public: intzigZagArrays(int n, int l, int r){ constexprint MOD = 1'000'000'007; int k = r - l + 1; vector<int> f(k, 1); // 初始长度 1,每个值有 1 种可能
for (int i = 1; i < n; i++) { if (i % 2) { // 奇数步 → 需要上升 longlong pre = 0; for (int j = 0; j < k; j++) { int v = f[j]; f[j] = pre % MOD; // f[j] = 所有小于 j 的和 pre += v; } } else { // 偶数步 → 需要下降 longlong suf = 0; for (int j = k - 1; j >= 0; j--) { int v = f[j]; f[j] = suf % MOD; // f[j] = 所有大于 j 的和 suf += v; } } }