dp的hard题,写出状态定义和状态转移都不是最难的。basecase很难,而且最好从dfs->记忆化->dp的顺序循序渐进
class Solution {
public:
bool canCross(vector<int>& stones) {
//dfs超时就考虑dp
//状态定义:dp[i][k]的含义是当前在第 i 个位置,并且是以步长 k 跳到位置 i 时,是否到达最后一块石子。
//状态转移方程:根据上一位置结合步长变化是-1,0,1来得到的
//dp[i][k] = dp[j][k-1] || dp[j][k] || dp[j][k+1]
//含义分别是可从 f[j][k - 1]f[j][k−1] 状态而来:先是经过 k - 1k−1 的跳跃到达位置 jj,再在原步长的基础上 +1,跳到了位置 ii。
//可从 f[j][k]f[j][k] 状态而来:先是经过 kk 的跳跃到达位置 jj,维持原步长不变,跳到了位置 ii。
//可从 f[j][k + 1]f[j][k+1] 状态而来:先是经过 k + 1k+1 的跳跃到达位置 jj,再在原步长的基础上 -1,跳到了位置 ii。
//basecase:我们知道起始状态是「经过步长为 1」的跳跃到达「位置 1」,如果从起始状态出发,存在一种方案到达最后一块石子的话,那么必然存在一条反向路径,它是以从「最后一块石子」开始,并以「某个步长 kk」开始跳跃,最终以回到位置 1。
//因此我们可以设 f[1][1] = truef[1][1]=true,作为我们的起始值。
vector<vector<bool>> dp(stones.size(),vector<bool>(stones.size()));
int n = stones.size();
if(stones[1] != 1) return false;
dp[1][1] = true;
for(int i = 2; i < n; ++i){
for(int j = 1; j < i; ++j){
int k = stones[i] - stones[j];
if(k <= j+1) dp[i][k] = dp[j][k-1] || dp[j][k] || dp[j][k+1];
}
}
for(int i = 1; i < n; ++i){
if(dp[n-1][i]) return true;
}
return false;
}
};