A chess knight can move as indicated in the chess diagram below:

 .           

This time, we place our chess knight on any numbered key of a phone pad (indicated above), and the knight makes N-1 hops.  Each hop must be from one key to another numbered key.

Each time it lands on a key (including the initial placement of the knight), it presses the number of that key, pressing N digits total.

How many distinct numbers can you dial in this manner?

Since the answer may be large, output the answer modulo 10^9 + 7.

Example 1:

Input: 1
Output: 10

Example 2:

Input: 2
Output: 20

Example 3:

Input: 3
Output: 46

Note:

  • 1 <= N <= 5000

这道题说是有一种骑士拨号器,在一个电话拨号盘上跳跃,其跳跃方式是跟国际象棋中的一样,不会国际象棋的童鞋可以将其当作中国象棋中的马,马走日象飞田。这个骑士可以放在 10 个数字键上的任意一个,但其跳到的下一个位置却要符合其在国际象棋中的规则,也就是走日。现在给了一个整数N,说是该骑士可以跳N次,问能拨出多个不同的号码,并且提示了结果要对一个超大数字取余。看到这里,对于各位刷题老司机来说,肯定能反应过来要用动态规划 Dynamic Programming 了吧,因为数字可能巨大无比,强行暴力递归破解可能会爆栈。这里使用一个二维数组 dp,其中 dp[i][j] 表示骑士第i次跳到数字j时组成的不同号码的个数,那么最终所求的就是将 dp[N-1][j] 累加起来,j的范围是0到9。接下来看状态转移方程怎么写,当骑士在第i次跳到数字j时,考虑其第 i-1 次是在哪个位置,可能有多种情况,先来分析拨号键盘的结构,找出从每个数字能到达的下一个位置,可得如下关系:

0 -> 4, 6

1 -> 6, 8

2 -> 7, 9

3 -> 4, 8

4 -> 3, 9, 0

5 ->

6 -> 1, 7, 0

7 -> 2, 6

8 -> 1, 9

9 -> 4, 2

可以发现,除了数字5之外,每个数字都可以跳到其他位置,其中4和6可以跳到三个不同位置,其他都只能取两个位置。反过来想,可以去的位置,就表示也可能从该位置回来,所以根据当前的位置j,就可以在数组中找到上一次骑士所在的位置,并将其的 dp 值累加上即可,这就是状态转移的方法,由于第一步是把骑士放到任意一个数字上,就要初始化 dp[0][j] 为1,然后进行状态转移就行了,记得每次累加之后要对超大数取余,最后将 dp[N-1][j] 累加起来的时候,也要对超大数取余,参见代码如下:

解法一:

class Solution {
public:
int knightDialer(int N) {
int res = 0, M = 1e9 + 7;
vector<vector<int>> dp(N, vector<int>(10));
vector<vector<int>> path{{4, 6}, {6, 8}, {7, 9}, {4, 8}, {3, 9, 0}, {}, {1, 7, 0}, {2, 6}, {1, 9}, {4, 2}};
for (int i = 0; i < 10; ++i) dp[0][i] = 1;
for (int i = 1; i < N; ++i) {
for (int j = 0; j <= 9; ++j) {
for (int idx : path[j]) {
dp[i][j] = (dp[i][j] + dp[i - 1][idx]) % M;
}
}
}
for (int i = 0; i < 10; ++i) res = (res + dp.back()[i]) % M;
return res;
}
};

我们也可以用递归+记忆数组的方式来写,整体思路和迭代的方法并没有什么区别,之前类似的题目也不少,就不多解释了,可以对照上面的讲解和代码来理解,参见代码如下:


解法二:

class Solution {
public:
int knightDialer(int N) {
int res = 0, M = 1e9 + 7;
vector<vector<int>> memo(N + 1, vector<int>(10));
vector<vector<int>> path{{4, 6}, {6, 8}, {7, 9}, {4, 8}, {3, 9, 0}, {}, {1, 7, 0}, {2, 6}, {1, 9}, {4, 2}};
for (int i = 0; i < 10; ++i) {
res = (res + helper(N - 1, i, path, memo)) % M;
}
return res;
}
int helper(int n, int cur, vector<vector<int>>& path, vector<vector<int>>& memo) {
if (n == 0) return 1;
if (memo[n][cur] != 0) return memo[n][cur];
int res = 0, M = 1e9 + 7;
for (int idx : path[cur]) {
res = (res + helper(n - 1, idx, path, memo)) % M;
}
return memo[n][cur] = res;
}
};

Github 同步地址:

https://github.com/grandyang/leetcode/issues/935

类似题目:

Letter Combinations of a Phone Number

参考资料:

https://leetcode.com/problems/knight-dialer/

https://leetcode.com/problems/knight-dialer/discuss/189265/Concise-Java-DP-Solution

https://leetcode.com/problems/knight-dialer/discuss/189271/Java-Top-Down-Memo-DP-O(N)

https://leetcode.com/problems/knight-dialer/discuss/190787/How-to-solve-this-problem-explained-for-noobs!!!

[LeetCode All in One 题目讲解汇总(持续更新中...)](https://www.cnblogs.com/grandyang/p/4606334.html)

[LeetCode] 935. Knight Dialer 骑士拨号器的更多相关文章

  1. LeetCode 935. Knight Dialer

    原题链接在这里:https://leetcode.com/problems/knight-dialer/ 题目: A chess knight can move as indicated in the ...

  2. [Swift]LeetCode935. 骑士拨号器 | Knight Dialer

    A chess knight can move as indicated in the chess diagram below:  .            This time, we place o ...

  3. [Leetcode][动态规划] 第935题 骑士拨号器

    一.题目描述 国际象棋中的骑士可以按下图所示进行移动:                           我们将 “骑士” 放在电话拨号盘的任意数字键(如上图所示)上,接下来,骑士将会跳 N-1 步 ...

  4. 【LeetCode】935. Knight Dialer 解题报告(Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 动态规划TLE 空间换时间,利用对称性 优化空间复杂 ...

  5. 【leetcode】935. Knight Dialer

    题目如下: A chess knight can move as indicated in the chess diagram below:  .            This time, we p ...

  6. 935. Knight Dialer

    A chess knight can move as indicated in the chess diagram below:  .            This time, we place o ...

  7. 02_电话拨号器intent说明

    怎么在第一个Activity打开第二个Activity?在一个Activity中打开另外一个Activity,实际上之前已经做过,就是电话拨号器. package com.itheima.callne ...

  8. Mono for Android—初体验之“电话拨号器”

    1.Main.axml文件: <?xml version="1.0" encoding="utf-8"?><LinearLayout xmln ...

  9. Android 笔记 day2 拨号器

随机推荐

  1. Git 常用资源

    库管理 克隆库 git clone https://github.com/php/php-src.git git clone --depth=1 https://github.com/php/php- ...

  2. 你有哪些相见恨晚的Chrome 扩展?

    「Chrome 没插件,香味少一半」,本期我们就来一起盘点一下chrome上那些相见恨晚的扩展. 1 JSONView2 Adblock Plus3 Keylines4 彩云小译5 单词发现者6 鼠标 ...

  3. Linux系统发行版本及其区别

    1 Linux系统组成 Linux操作系统=Linux内核+GNU软件及系统软件+必要的应用程序.下表为Linux系统各组成部分的贡献人员: Linux内核 GNU组件(gcc.bash) 其他必要应 ...

  4. tp5.1 请求时间格式化

    当前时间:{$Request.time|date='Y-m-d H:i:s'} 注意database.php的配置!记录一下!

  5. 挖SRC逻辑漏洞心得分享

    文章来源i春秋 白帽子挖洞的道路还漫长的很,老司机岂非一日一年能炼成的. 本文多处引用了 YSRC 的 公(qi)开(yin)漏(ji)洞(qiao).挖SRC思路一定要广!!!!漏洞不会仅限于SQL ...

  6. 先导篇:SpringCloud介绍篇

    1.什么是SpringCloud Spring Cloud是一系列框架的有序集合.它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册.配置中心.消息总线.负载 ...

  7. 使用cookie

    概述 虽说在现代Web开发过程中讨论Cookie有些不合时宜,但是这是开发人员如今可以使用的最古老.最稳定的客户端存储形式.当然,我们并不推荐使用Cookie,只是说它是一种选择. Cookie于19 ...

  8. nx-admin1.2版本发布

    nx-admin 是一个开源的管理系统前端集成方案 github地址 nx-admin的初心 组件更易用, 让每个小白快速上手, 最大程度上帮助个人,企业节省时间成本和费用开支. 尽管这个过程不简单, ...

  9. 纯JS实现KeyboardNav(学习笔记)一

    纯JS实现KeyboardNav(学习笔记)一 这篇博客只是自己的学习笔记,供日后复习所用,没有经过精心排版,也没有按逻辑编写 GitHub项目源码 预览地址 最终效果 KeyboardNav使用指南 ...

  10. c语言之学生管理系统

    0x00 学生管理系统 说到学生管理系统,对于每一个初学c语言的人都是一道不得不过的砍.不过,学习c,我觉得每个人都应该写一个学生管理系统,至于为什么,我想倘若连一个学生管理系统都写不好,哪么他的c是 ...