[LeetCode] 935. Knight Dialer

本文介绍了一种使用动态规划解决骑士拨号器问题的方法,详细解释了如何构建dp数组及递推关系式,提供了完整的实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原题链接: https://leetcode.com/problems/knight-dialer/

1. 题目介绍

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 , and the knight makes N-1 hops. Each hop must be from one key to another numbered key.

这次我们把这个骑士放到电话号码键盘上。最初,骑士在号码键盘的任意数字上。接下来他要走 N-1步,每一步必须从一个数字跳到另一个数字
在这里插入图片描述
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.

Note:
1 <= N <= 5000

骑士每次落到一个数字上时(包括他最初所在的数字),就会按下那个号码。一共会按出 N 位号码。

给出数字 N ,1 <= N <= 5000 ,代表骑士按出的号码位数。
问:骑士可以按出多少种不同的号码?

由于最后的结果可能会很大,因此答案对 109+ 7 取余。

2. 解题思路

注: 本解题思路参考了 https://leetcode.com/problems/knight-dialer/solution/

这个题我想了很久。动态规划最难的两个地方在于构造 dp 数组和推导递推关系式。而这个递推关系式我一直没有想出来。看了官方的题解后才恍然大悟。在这里我直接采用 LeetCode 题解中的思路吧。

我们可以设定一个函数 f ( start , n ) ,f ( start , n )代表了骑士从数字 start 开始,按下 n 位号码的不同走法的数量,这 n 位号码中的第一位就是数字start。

通过观察,可以发现:
从 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 , 3 },
从 9 出发,只能走到{2 , 4 }.

因此,可以据此得到 f( start , n )的递推关系式。
比如: f( 1 , n ) = f( 6 , n-1 ) + f( 8, n-1 ).
f( 4 , n ) = f( 3 , n-1 ) + f( 9, n-1 ) + f( 0, n-1 ).

于是,我们可以构造一个二维数组 dp[ ][ ] 用来存放 f( start , n )的值。不用每次用到 f( start , n )的值都去重新计算。
dp[ n ][ start ] 中存放 f( start , n )的值。(注意start 是列,n 是行)
最后的答案就是 f(0, N) + f(1, N) + … + f(9, N) 的和。

实现代码

class Solution {
    public int knightDialer(int N) {
		int [][] dp = new int[N+1][10];
		//对于较大的数,可以用_分隔
		int MOD = 1_000_000_007;
		int [][] moves = {
					{4 , 6 },//0
					{6 , 8 },//1
					{7 , 9 },//2
					{4 , 8 },//3
					{3 , 9, 0 },//4
					{ },//5
					{1 , 7 ,0 },//6
					{2 , 6 },//7
					{1 , 3 },//8
					{2 , 4 },//9
			};
		
		//填充dp[1]这一行中的每个元素都是1
        //意味着当 N = 1 的时候,只有1种走法:停在原地
        //dp[0]这一行是没有用的,因为N不可能为0
        Arrays.fill(dp[1], 1);
       
        for(int i = 2 ; i<=N ; i ++ ) {
        	for(int j = 0 ; j<=9 ; j++) {
        		for(int k = 0 ; k< moves[j].length; k++) {
        			dp[i][j] += dp[i-1][ moves[j][k] ];
        			dp[i][j] %= MOD;
        		}
        	}
        }
        
        long ans = 0;
        for(int i = 0;i<10;i++) {
        	ans += dp[N][i];
        }
        ans %= MOD;
       	return (int)ans;
    }
}

3. 参考资料

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值