go动态规划需注意三要点:状态定义贴合本质、子问题边界卡准、避开map[int]int零值陷阱;climbstairs支持1/2/3步时,dp[0]=1、dp[1]=1、dp[2]=2,切片长度应为n+1;map查值须用ok判断,一维背包需倒序遍历,字符串dp索引应基于长度并转[]rune。

Go 里写动态规划,不靠背模板,靠三件事:状态定义是否贴合问题本质、子问题边界有没有卡准、map[int]int 的零值陷阱有没有绕开。
climbStairs 支持 1/2/3 步时,基础 case 和数组长度怎么设
状态转移是 dp[i] = dp[i-1] + dp[i-2] + dp[i-3],但这个公式只在 i >= 3 时成立。前三个位置必须手动填对:
-
dp[0] = 1:站在起点不动算 1 种方式(不是 0) -
dp[1] = 1:只能跳 1 步 -
dp[2] = 2:1+1 或直接跳 2 步
如果用切片实现,别写 make([]int, n) —— dp[3] 会访问 dp[0],下标越界。应写 make([]int, n+1),并确保 n ≥ 0;若 n 很小(比如 0、1、2),直接返回预设值,别进循环。
用 map 做记忆化时,memo[n] > 0 判断为什么总出错
Go 的 map[int]int 查未存的 key 会返回 0,和“计算结果恰好是 0”无法区分。比如某个子问题合法解就是 0,memo[n] > 0 就会跳过它,导致重复计算甚至死递归。
- 安全写法:
if val, ok := memo[n]; ok { return val } - 更省心的替代:
memo := make([]int, n+1)配done := make([]bool, n+1),用done[n]显式标记是否算过 - 别用
memo[n] != 0或memo[n] > -1这类依赖零值的判断
0-1 背包用一维滚动数组,为什么必须倒序遍历容量
二维状态是 dp[i][w] = max(dp[i-1][w], dp[i-1][w-weight[i]] + value[i])。压成一维后,dp[w] 必须始终代表“前 i−1 个物品”的结果,否则就变成完全背包了。
- 正序遍历(
w = weight[i]; w ):更新 <code>dp[w-weight[i]]时,它可能已是本轮新值,相当于重复选了第 i 个物品 - 倒序遍历(
w = capacity; w >= weight[i]; w--):保证dp[w-weight[i]]拿到的是上一轮旧值 - 初始化要写
dp := make([]int, capacity+1),不是make([]int, 0, capacity+1),否则dp[w]索引 panic
字符串类 DP(如 LCS、回文子序列)索引偏移怎么不出错
这类题的 dp[i][j] 通常定义为“s[0:i] 和 t[0:j] 的解”,即 i、j 是长度,不是下标。这意味着:
- 数组得开
make([][]int, len(s)+1)和make([]int, len(t)+1) - 循环范围是
i = 0 to len(s)(含),不是len(s)-1 - 字符比较要用
s[i-1] == t[j-1],因为i和j指的是子串长度 - 输入含中文或 emoji?先转
[]rune,否则string[i]按字节取,必乱码或越界
最常被忽略的其实是初始化逻辑:比如最长回文子序列要求 dp[i][i] = 1,但如果你的循环从 i = 0 开始且没单独处理对角线,这一行就永远是 0。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











