【LeetCode】935. Knight Dialer 解题报告(Python)
作者: 负雪明烛
id: fuxuemingzhu
个人博客: http://fuxuemingzhu.cn/
目录
- 题目描述
- 题目大意
- 解题方法
- 动态规划TLE
- 空间换时间,利用对称性
- 优化空间复杂度
- 相似题目
- 参考资料
- 日期
题目地址:https://leetcode.com/problems/knight-dialer/description/
题目描述
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
题目大意
马的初始位置可以在拨号按键的任意位置,现在要让它走N - 1步,问这个马能产生出多少种不同的拨号号码?
解题方法
动态规划TLE
本周周赛第二题,卡了我好久啊!好气!
这个题本身肯定是动态规划题目,设置dp数组为当前步以每个按键结尾的状态数。所以我使用了一个4×3的二维数组,需要注意的是左下角和右下角的位置不可能到达,设置它的数值为0.状态转移方程很好求得,那就是把上一步可能存在的位置状态累加在一起就成了当前位置的状态数。
问题是会超时啊!甚至可能会超过内存限制!
先上一份很容易想到的,但是会超时TLE的代码:
时间复杂度是O(N),空间复杂度O(N).
class Solution:def knightDialer(self, N):""":type N: int:rtype: int"""self.ans = dict()self.ans[0] = 10board = [[1] * 3 for _ in range(4)]board[3][0] = board[3][3] = 0pre_dict = {(i, j) : self.prevMove(i, j) for i in range(4) for j in range(3)}for n in range(1, N):new_board = copy.deepcopy(board)for i in range(4):for j in range(3):cur_move = 0for x, y in pre_dict[(i, j)]:cur_move = (cur_move + board[x][y]) % (10 ** 9 + 7)new_board[i][j] = cur_moveboard = new_boardreturn sum([board[i][j] for i in range(4) for j in range(3)]) % (10 ** 9 + 7)def prevMove(self, i, j):if (i, j) == (3, 0) or (i, j) == (3, 2):return []directions = [(-2, 1), (-1, 2), (1, 2), (2, 1), (2, -1), (1, -2), (-1, -2), (-2, -1)]res = []for d in directions:x, y = i + d[0], j + d[1]if 0 <= x < 4 and 0 <= y < 3 and (x, y) != (3, 0) and (x, y) != (3, 2):res.append((x, y))return res
在比赛的时候剩下的一个小时都在优化这个题,个人感觉这个题卡时间卡的有点太严了,上面这个做法应该是标准做法吧,通过不了,需要一些奇技淫巧才能通过。
空间换时间,利用对称性
这是我在比赛最后的时间通过的代码,把所有状态给初始化了,这样好处是可以不用在循环中不停地copy原来的棋盘状态了,同时利用了对称性,只需要求出4个位置(1,2,4,0)的状态,其余状态可以直接利用对称性得到。
还有一个优化的地方在于在每次的过程中进行取模!虽然取模运算是耗时的运算,但是数字很大的时候,大整数既占空间又占时间,所以取模!
经过上面的优化勉强通过了,真是不容易,我觉得这个题非常不友好,因为同样的Java代码可以不做任何优化就通过了。这个题在N很大的时候还会告诉我内存超了……简直了。。
时间复杂度是O(N),空间复杂度O(N).总时间1500ms。
class Solution:def knightDialer(self, N):""":type N: int:rtype: int"""self.ans = dict()self.ans[0] = 10board = [[[1] * 3 for _ in range(4)] for _ in range(N)]board[0][3][0] = board[0][3][2] = 0pre_dict = {(i, j) : self.prevMove(i, j) for i in range(4) for j in range(3)}for n in range(1, N):for i in range(2):cur_move = 0for x, y in pre_dict[(i, 0)]:cur_move += board[n - 1][x][y]board[n][i][0] = cur_move % (10 ** 9 + 7)cur_move = 0for x, y in pre_dict[(0, 1)]:cur_move += board[n - 1][x][y]board[n][0][1] = cur_move % (10 ** 9 + 7)cur_move = 0for x, y in pre_dict[(3, 1)]:cur_move += board[n - 1][x][y]board[n][3][1] = cur_move % (10 ** 9 + 7)board[n][4][0] = board[n][0][0]board[n][0][2] = board[n][0][0]board[n][5][1] = 0board[n][6][2] = board[n][7][0]board[n][8][1] = board[n][0][1]board[n][9][2] = board[n][0][2]board[n][3][0] = board[n][3][2] = 0return (board[N - 1][0][0] * 4 + board[N - 1][0][1] * 2 + board[N - 1][10][0] * 2 + board[N - 1][3][1] + board[N - 1][11][1]) % (10 ** 9 + 7)def prevMove(self, i, j):if (i, j) == (3, 0) or (i, j) == (3, 2):return []directions = [(-2, 1), (-1, 2), (1, 2), (2, 1), (2, -1), (1, -2), (-1, -2), (-2, -1)]res = []for d in directions:x, y = i + d[0], j + d[1]if 0 <= x < 4 and 0 <= y < 3 and (x, y) != (3, 0) and (x, y) != (3, 2):res.append((x, y))return res
优化空间复杂度
上面的做法我一直在想着优化时间复杂度,事实上,每个状态只和之前的状态有关,所以很容易想到优化空间复杂度。
使用10个变量,分别保存每个位置能取到的状态数,然后人为的把每个状态能通过其他的状态得到的代码给写出来就行了。
代码如下,真的很简洁,为什么我没有想到优化空间!!优化之后时间降到了264 ms,这个告诉我们,优化空间同样可以大规模地降低时间,如果DP问题超时的话,优先考虑空间!
时间复杂度是O(N),空间复杂度O(1).时间264 ms.
class Solution:def knightDialer(self, N):""":type N: int:rtype: int"""if N == 1: return 10x1 = x2 = x3 = x4 = x5 = x6 = x7 = x8 = x9 = x0 = 1MOD = 10 ** 9 + 7for i in range(N - 1):x1, x2, x3, x4, x5, x6, x7, x8, x9, x0 = (x6 + x8) % MOD,\(x7 + x9) % MOD, (x4 + x8) % MOD, (x3 + x9 + x0) % MOD, 0, (x1 + x7 + x0) % MOD,\(x2 + x6) % MOD, (x1 + x3) % MOD, (x2 + x4) % MOD, (x4 + x6) % MODreturn (x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x0) % MOD
如果在上面的解法上再利用好对称性的话,可以把时间再次降低到160 ms。
时间复杂度是O(N),空间复杂度O(1).时间160 ms。
class Solution:def knightDialer(self, N):""":type N: int:rtype: int"""if N == 1: return 10x1 = x2 = x3 = x4 = x5 = x6 = x7 = x8 = x9 = x0 = 1MOD = 10 ** 9 + 7for i in range(N - 1):x1, x2, x4, x0 = (x6 + x8) % MOD, (x7 + x9) % MOD, (x3 + x9 + x0) % MOD, (x4 + x6) % MODx3, x5, x6, x7, x8, x9 = x1, 0, x4, x1, x2, x1return (x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x0) % MOD
相似题目
688. Knight Probability in Chessboard
参考资料
https://leetcode.com/problems/knight-dialer/discuss/189252/O(logN)
日期
2018 年 11 月 4 日 —— 下雨的周日
【LeetCode】935. Knight Dialer 解题报告(Python)相关推荐
- 【LeetCode】3Sum Closest 解题报告
[题目] Given an array S of n integers, find three integers in S such that the sum is closest to a give ...
- 【LeetCode】77. Combinations 解题报告(Python C++)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 方法一:递归 方法二:回溯法 日期 题目地址:htt ...
- Leetcode 1190. Reverse Substrings Between Each Pair of Parentheses解题报告(python)
1190. Reverse Substrings Between Each Pair of Parentheses Reverse Substrings Between Each Pair of Pa ...
- 【LeetCode】517. 超级洗衣机 解题报告 (python)
原题地址:https://leetcode-cn.com/problems/super-washing-machines/submissions/ 题目描述: 假设有 n 台超级洗衣机放在同一排上.开 ...
- 935.Knight Dialer [JavaScript]
一.题目 A chess knight can move as indicated in the chess diagram below: This time, we place our chess ...
- LeetCode Maximum Product Subarray 解题报告
LeetCode 新题又更新了.求:最大子数组乘积. https://oj.leetcode.com/problems/maximum-product-subarray/ 题目分析:求一个数组,连续子 ...
- 解题报告——Python编程:从入门到实践_动手试一试_参考答案(第四章)
注意: 做4.11时,习惯性的使用等号解题,结果发现:用等号会关联二者地址,修改其中任意一位,另一方也被修改, 因此需要使用切片方法去解题. # 习题4.1:想出至少三种你喜欢的披萨,将其名称存储在一 ...
- LeetCode 705 Design HashSet 解题报告
题目要求 Design a HashSet without using any built-in hash table libraries. To be specific, your design s ...
- [leetcode] 28. Implement strStr() 解题报告
题目链接:https://leetcode.com/problems/implement-strstr/ Implement strStr(). Returns the index of the fi ...
最新文章
- Spark技术栈有哪些组件,每个组件都有什么功能,适合什么应用场景?
- 算法提高课-图论-单源最短路的综合应用-AcWing 342. 道路与航线:最短路dijkstra、拓扑排序 、综合题、好题
- mysql 分表全局id_MySQL分庫分表環境下全局ID生成方案
- 关系数据库SQL语言核心功能
- 检测raid类型和磁盘坏道脚本
- 企业安全建设之自动化代码扫描(代码审计)
- 校友会2019中国大学计算机,校友会2019中国大学一流专业排名800强出炉,北大清华复旦前三...
- 【To Understand】程序员面试金典——番外篇之洪水
- 从日本动漫看项目管理
- 【大厂面试】面试官看了赞不绝口的Redis笔记(三)分布式篇
- 使用Acrobat剪切pdf文件中矢量图
- 常用网站攻击手段及防御方法
- 正和岛青年徽商正和塾小组2021年首聚—走进掌榕
- 你所不知道的OneNote骚操作 | 告别枯燥 OneNote让数学教学更有趣
- COLA 4.0:应用架构的最佳实践
- 把Excel批注的“红三角”放在单元格左上角_44个Excel使用技巧:怪不得同事表格类工作那么快完成,学以致用...
- 上海旅游-徐家汇教堂
- 假设二叉树采用二叉链存储结构,编写一个算法,求出二叉树中的叶子结点数,并设计主函数调用上述算法。
- 苹果iOS时间显示NaN
- 6款电脑必备软件,每一款都非常好用,当场起飞!