Knight Dialer - A Classic Dynamic Programming Problem
This was a very interesting problem, and a classic application
of Dynamic Programming. Here is the problem: https://leetcode.com/problems/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
The 10^9 + 7 to me is always the strongest indication that
the solution should be done via DP. The also indication is that the N is very
large (5000), hence if we try all the possibilities, we’re possibly talking
about 9^5000, which is not tractable.
The way to think about the solution for this problem is the
following: suppose that you have the solution for N-1. You know for each cell
from 0..9 the solution for N-1 hops. In this case how can you get the solution
for N hops? Well, what you can do is that for each cell, say cell(0), how many
possibilities can you have? The number of possibilities for cell(0) is whatever
the N-1 solution is for cell(4) plus whatever the N-1 solution is for cell(6).
Once you do that for all cells, then you add them all up and you have the
solution for N.
With that in mind, we build the solution by construction.
The very first thing to do is to create a map mapping each cell to the corresponding
cells based on how the knight moves (as I said above, for example, 0 maps to 4
and 6). That’s where you use the information about how the knight moves. Then
you construct the solution for N=1 (trivial, no hops). From that point one,
build the solution for 2…N. I use a hash table just because I find it super
easy to manipulate the data structure.
Looking at the space complexity, it is a constant space. Time
would be linear: N (max: 5000) * 10 (fixed) * 3 (max number of mapped cells).
Code is down below – cheers!!! ACC.
public class Solution { public int KnightDialer(int N) { Hashtable map = new Hashtable(); map.Add(0, new int[] { 4, 6, }); map.Add(1, new int[] { 6, 8 }); map.Add(2, new int[] { 7, 9 }); map.Add(3, new int[] { 4, 8 }); map.Add(4, new int[] { 0, 3, 9 }); map.Add(5, new int[] { }); map.Add(6, new int[] { 0, 1, 7 }); map.Add(7, new int[] { 2, 6 }); map.Add(8, new int[] { 1, 3 }); map.Add(9, new int[] { 2, 4 }); //DP Hashtable previous = new Hashtable(); Hashtable current = new Hashtable(); //N=1 for (int i = 0; i < 10; i++) { previous.Add(i, 1); } current = (Hashtable)previous.Clone(); //N>1 for (int i = 1; i < N; i++) { current = new Hashtable(); for (int j = 0; j < 10; j++) { current.Add(j, 0); int[] mapArray = (int[])map[j]; for (int z = 0; z < mapArray.Length; z++) { current[j] = ((int)current[j] + (int)previous[mapArray[z]]) % (1000000000 + 7); } } previous = (Hashtable)current.Clone(); } int sum = 0; for (int i = 0; i < 10; i++) { sum = (sum + (int)current[i]) % (1000000000 + 7); } return sum; } }
Nice article admin thanks for share your atricle keep share your knowledge i am waiting for your new post check mens winter jackets polo shirts kindly review and reply me
ReplyDeletelove this problem! Interestingly an O(1) space solution is even easier to understand:
ReplyDeleteMOD = 10**9 + 7
class Solution:
def knightDialer(self, N: int) -> int:
d0 = d1 = d2 = d3 = d4 = d5 = d6 = d7 = d8 = d9 = 1
for _ in range(N-1):
d0, d1, d2, d3, d4, d5, d6, d7, d8, d9 = \
(d4 + d6) % MOD, \
(d6 + d8) % MOD, \
(d7 + d9) % MOD, \
(d4 + d8) % MOD, \
(d0 + d3 + d9) % MOD, \
0, \
(d0 + d1 + d7) % MOD, \
(d2 + d6) % MOD, \
(d1 + d3) % MOD, \
(d2 + d4) % MOD
return (d0 + d1 + d2 + d3 + d4 + d5 + d6 + d7 + d8 + d9) % MOD
And similar to fibonacci sequence optimization to achieve O(N*log(N)) time complexity these additions can be first replaced with matrix multiplication and fast exponentiation algorithm, but I'm too lazy for that :) This solution is already fast enough.
kissanime
ReplyDeletechia anime
soul anime
anime Gogo
Gogo anime
Kiss Anime
Watch Anime
9anime
This comment has been removed by the author.
ReplyDeleteCool solution. Moving away from the hashtable (required boxing / unboxing) to a dictionary and moving to arrays instead of hashtable for the current and previous trackers reduces the runtime by 5x.
ReplyDeleteint mod=(int)1e9+7;
public int KnightDialer(int N) {
Dictionary map = new Dictionary();
map.Add(0, new int[] { 4, 6, });
map.Add(1, new int[] { 6, 8 });
map.Add(2, new int[] { 7, 9 });
map.Add(3, new int[] { 4, 8 });
map.Add(4, new int[] { 0, 3, 9 });
map.Add(5, new int[] { });
map.Add(6, new int[] { 0, 1, 7 });
map.Add(7, new int[] { 2, 6 });
map.Add(8, new int[] { 1, 3 });
map.Add(9, new int[] { 2, 4 });
//DP
int[] previous = new int[10];
int[] current = new int[10];
//N=1
for (int i = 0; i < 10; i++)
{
previous[i] = 1;
}
current = (int[])previous.Clone();
//N>1
for (int i = 1; i < N; i++)
{
current = new int[10];
for (int j = 0; j < 10; j++)
{
current[j]= 0;
int[] mapArray = map[j];
for (int z = 0; z < mapArray.Length; z++)
{
current[j] = (current[j] + previous[mapArray[z]]) % mod;
}
}
previous = (int[])current.Clone();
}
int sum = 0;
for (int i = 0; i < 10; i++)
{
sum = (sum + current[i]) % mod;
}
return sum;
}