【LeetCode】935. Knight Dialer 解题报告(Python)
作者: 负雪明烛
id: fuxuemingzhu
个人博客: http://fuxuemingzhu.cn/
题目地址: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] = 10
board = [[1] * 3 for _ in range(4)]
board[3][0] = board[3][3] = 0
pre_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 = 0
for x, y in pre_dict[(i, j)]:
cur_move = (cur_move + board[x][y]) % (10 ** 9 + 7)
new_board[i][j] = cur_move
board = new_board
return 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] = 10
board = [[[1] * 3 for _ in range(4)] for _ in range(N)]
board[0][3][0] = board[0][3][2] = 0
pre_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 = 0
for 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 = 0
for 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 = 0
for 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] = 0
board[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] = 0
return (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 10
x1 = x2 = x3 = x4 = x5 = x6 = x7 = x8 = x9 = x0 = 1
MOD = 10 ** 9 + 7
for 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) % MOD
return (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 10
x1 = x2 = x3 = x4 = x5 = x6 = x7 = x8 = x9 = x0 = 1
MOD = 10 ** 9 + 7
for i in range(N - 1):
x1, x2, x4, x0 = (x6 + x8) % MOD, (x7 + x9) % MOD, (x3 + x9 + x0) % MOD, (x4 + x6) % MOD
x3, x5, x6, x7, x8, x9 = x1, 0, x4, x1, x2, x1
return (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] 935. Knight Dialer 骑士拨号器
A chess knight can move as indicated in the chess diagram below: . This time, we place o ...
- LeetCode 935. Knight Dialer
原题链接在这里:https://leetcode.com/problems/knight-dialer/ 题目: A chess knight can move as indicated in the ...
- 【LeetCode】120. Triangle 解题报告(Python)
[LeetCode]120. Triangle 解题报告(Python) 作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 题目地址htt ...
- LeetCode 1 Two Sum 解题报告
LeetCode 1 Two Sum 解题报告 偶然间听见leetcode这个平台,这里面题量也不是很多200多题,打算平时有空在研究生期间就刷完,跟跟多的练习算法的人进行交流思想,一定的ACM算法积 ...
- 【LeetCode】Permutations II 解题报告
[题目] Given a collection of numbers that might contain duplicates, return all possible unique permuta ...
- 【LeetCode】Island Perimeter 解题报告
[LeetCode]Island Perimeter 解题报告 [LeetCode] https://leetcode.com/problems/island-perimeter/ Total Acc ...
- 【LeetCode】01 Matrix 解题报告
[LeetCode]01 Matrix 解题报告 标签(空格分隔): LeetCode 题目地址:https://leetcode.com/problems/01-matrix/#/descripti ...
- 【LeetCode】Largest Number 解题报告
[LeetCode]Largest Number 解题报告 标签(空格分隔): LeetCode 题目地址:https://leetcode.com/problems/largest-number/# ...
- 【LeetCode】Gas Station 解题报告
[LeetCode]Gas Station 解题报告 标签(空格分隔): LeetCode 题目地址:https://leetcode.com/problems/gas-station/#/descr ...
随机推荐
- python(3)跳过第一行(多行)读入数据
查了下资料,常见两种办法,一是设置行号,再者是利用python自带的itertools工具. 这里推荐一种新的方法,直接使用readline()函数就搞定. 示例: 创建一个文本文件,内容如下: 1 ...
- 31-Longest Common Prefix
Longest Common Prefix My Submissions Difficulty: Easy Write a function to find the longest common pr ...
- 29-Regular Expression Matching-leetcode
'.' Matches any single character. '*' Matches zero or more of the preceding element. The matching sh ...
- express系列(1)概述
在 Node.js 出现之前,前后端的开发必须使用不同的语言进行.为此你需要学习多种的语言和框架.有了 Node.js 之后,你就可以使用一门语言在前后端开发中自由切换,这是最吸引人的地方. 什么是 ...
- 【Penetration】红日靶场(一)
nmap探查存活主机 nmap -sP 10.10.2.0/24 图片: https://uploader.shimo.im/f/cfuQ653BEvyA42FR.png!thumbnail?acce ...
- 3.3 rust HashMap
The type HashMap<K, V> stores a mapping of keys of type K to values of type V. It does this vi ...
- 【Service】【MiddleWare】【Message】rabbitMQ
1. 概念: 1.1. 消息型中间件:遵循AMQP协议(高级消息队列协议)AMQP 0-9-1 AMQP 1.0 1.2. 路由模型: direct topic fan-out headers 1.3 ...
- Spring Cloud中,如何解决Feign整合Hystrix第一次请求失败的问题
Spring Cloud中,Feign和Ribbon在整合了Hystrix后,可能会出现首次调用失败的问题,要如何解决该问题呢? 造成该问题的原因 Hystrix默认的超时时间是1秒,如果超过这个时间 ...
- SpringBoot 项目不加载 application.properties 配置文件
起因:新安装的idea第一次运行springboot项目报url错误(Failed to configure a DataSource: 'url' attribute is not specifie ...
- elasticSearch索引库查询的相关方法
package com.hope.es;import org.elasticsearch.action.search.SearchResponse;import org.elasticsearch.c ...