[LeetCode] 935. Knight Dialer 骑士拨号器
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)
[LeetCode All in One 题目讲解汇总(持续更新中...)](https://www.cnblogs.com/grandyang/p/4606334.html)
[LeetCode] 935. Knight Dialer 骑士拨号器的更多相关文章
- LeetCode 935. Knight Dialer
原题链接在这里:https://leetcode.com/problems/knight-dialer/ 题目: A chess knight can move as indicated in the ...
- [Swift]LeetCode935. 骑士拨号器 | Knight Dialer
A chess knight can move as indicated in the chess diagram below: . This time, we place o ...
- [Leetcode][动态规划] 第935题 骑士拨号器
一.题目描述 国际象棋中的骑士可以按下图所示进行移动: 我们将 “骑士” 放在电话拨号盘的任意数字键(如上图所示)上,接下来,骑士将会跳 N-1 步 ...
- 【LeetCode】935. Knight Dialer 解题报告(Python)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 动态规划TLE 空间换时间,利用对称性 优化空间复杂 ...
- 【leetcode】935. Knight Dialer
题目如下: A chess knight can move as indicated in the chess diagram below: . This time, we p ...
- 935. Knight Dialer
A chess knight can move as indicated in the chess diagram below: . This time, we place o ...
- 02_电话拨号器intent说明
怎么在第一个Activity打开第二个Activity?在一个Activity中打开另外一个Activity,实际上之前已经做过,就是电话拨号器. package com.itheima.callne ...
- Mono for Android—初体验之“电话拨号器”
1.Main.axml文件: <?xml version="1.0" encoding="utf-8"?><LinearLayout xmln ...
- Android 笔记 day2 拨号器
随机推荐
- Google Hacking --你真的会用Google吗?
你真的会用Google吗?Google Hacking提升工作效率 阅读本文需要6.66分钟 Google hacking,也叫作google dorking.如果在 Google 上搜索 Googl ...
- 《ASP.NET Core 高性能系列》Span<T>和Memory<T>
一.Span<T>概述 原文:Provides a type- and memory-safe representation of a contiguous region of arbit ...
- 《N诺机试指南》(八)日期、字符串、排序问题
1.日期问题: 输入: 例题: 代码: #include <stdio.h> #include <bits/stdc++.h> struct node{ int year, m ...
- AF(操作者框架)系列(3)-创建第一个Actor的程序
这节课的内容,语言描述基本是无趣的,就是一个纯程序编写,直接上图了. 如果想做其他练习,可参考前面的文章: https://zhuanlan.zhihu.com/p/105133597 1. 新建一个 ...
- PHPRAP v1.0.6 发布,修复因php7.1版本遗弃mcrypt扩展造成安装失败的BUG
PHPRAP,是一个PHP轻量级开源API接口文档管理系统,致力于减少前后端沟通成本,提高团队协作开发效率,打造PHP版的RAP. 更新记录 [修复]修复因php7.1版本遗弃mcrypt扩展造成安装 ...
- AJAX 的 Ajax返回数据之前的loading等待效果(gif效果等)
首先,我们通过ajax请求,向后台传递参数,然后后台经过一系列的运算之后向前台返还数据,我希望在等待数据成功返还之前可以展示一个loading.gif图 不废话,在页面上执行点击事件(<a sc ...
- Pycharm IDE安装及注册激活笔记(1)
一.Windows 下的安装及激活. 1.首先去Pycharm官网,或者直接输入网址:http://www.jetbrains.com/pycharm/download/#section=window ...
- IOS 7 UITableView cell lines不能靠左解决方法
添加一句:[UITableViewappearance].separatorInset=UIEdgeInsetsZero; 就可以解决啦.
- C++中的内存分配
C++提供下面两种方法分配和释放未构造的原始内存 (1)allocator 类,它提供可感知类型的内存分配 (2)标准库中的 operator new 和 operator delete,它们分配和释 ...
- 简说Python之flask-SQLAlchmey的web应用
目录 原生语句操作MySQL数据库 1.安装MySQL 2.MySQL设置用户和权限 3.用PyMySQL操纵MySQL数据库 4. CRUD增,删,改,查 使用SQLAlchemy 1.安装SQLA ...