又是一道很有意思的题目。
自己又没有想到图上面去:(
以后类似这种两点之间有关联的,可以联想到图上面去,把邻接表写出来就容易多了。
class Solution {
public:
int knightDialer(int N) {
vector<vector<int>> adj{{4, 6}, {6, 8}, {7, 9}, {4, 8}, {0, 3, 9}, {}, {0, 1, 7}, {2, 6}, {1, 3}, {2, 4}};
map<pair<int, int>, int> memo;
int cnt = 0;
for (int i = 0; i <= 9; ++i)
cnt = (cnt + helper(adj, memo, i, N-1)) % MOD;
return cnt;
}
private:
int MOD = 1e9 + 7;
int helper(vector<vector<int>>& adj, map<pair<int, int>, int>& memo, int pos, int steps) {
if (steps == 0)
return 1;
if (pos == 5)//为了提高效率,没有这一句也是对的
return 0;
auto key = make_pair(pos, steps);
if (memo.find(key) != memo.end())
return memo[key];
int cnt = 0;
for (int nextPos : adj[pos])
cnt = (cnt + helper(adj, memo, nextPos, steps-1)) % MOD;
memo[key] = cnt;
if (pos == 5)
cout << pos << " " << steps << " " << cnt << endl;
return cnt;
}
};
//想像成一个图的题目,可以走的两个键可以看成是相邻的两个点。把邻接表写出来,然后按着邻接表来走。这是没有想出来的关键。
//然后可以意识到重叠子问题:状态是<当前位置,还剩步数>
//对于5这个特殊键:如果最后一步走到5上,返回1;如果不是最后一步走到5上,那么就应该返回0,因为走不出去了,不能把所有步数都走完
后来发现超时。可能每次都make_pair时间有点久,而且map的查询时间也不是常数。
下面换成二维数组来存储。
class Solution {
public:
int knightDialer(int N) {
vector<vector<int>> adj{{4, 6}, {6, 8}, {7, 9}, {4, 8}, {0, 3, 9}, {}, {0, 1, 7}, {2, 6}, {1, 3}, {2, 4}};
vector<vector<int>> memo(10, vector<int>(N, -1));
int cnt = 0;
for (int i = 0; i <= 9; ++i)
cnt = (cnt + helper(adj, memo, i, N-1)) % MOD;
return cnt;
}
private:
int MOD = 1e9 + 7;
int helper(vector<vector<int>>& adj, vector<vector<int>>& memo, int pos, int steps) {
if (steps == 0)
return 1;
if (pos == 5)
return 0;
if (memo[pos][steps] != -1)
return memo[pos][steps];
int cnt = 0;
for (int nextPos : adj[pos])
cnt = (cnt + helper(adj, memo, nextPos, steps-1)) % MOD;
memo[pos][steps] = cnt;
return cnt;
}
};
//想像成一个图的题目,可以走的两个键可以看成是相邻的两个点。把邻接表写出来,然后按着邻接表来走。这是没有想出来的关键。
//然后可以意识到重叠子问题:状态是<当前位置,还剩步数>
//对于5这个特殊键:如果最后一步走到5上,返回1;如果不是最后一步走到5上,那么就应该返回0,因为走不出去了,不能把所有步数都走完
总的来说,想到用图,然后写出邻接表,就好多了。memo只是之后的优化