• [leetcode] 935 Knight Dialer 骑士拨号器


    题目:935. Knight Dialer 骑士拨号器

    问题描述

    国际象棋中的骑士可以按下图所示进行移动:
    骑士的走位
    拨号盘的键位

    这一次,我们将 “骑士” 放在电话拨号盘的任意数字键(如上图所示)上,接下来,骑士将会跳 N-1 步。每一步必须是从一个数字键跳到另一个数字键。
    每当它落在一个键上(包括骑士的初始位置),都会拨出键所对应的数字,总共按下 N 位数字。
    你能用这种方式拨出多少个不同的号码?
    因为答案可能很大,所以输出答案模 10^9 + 7

    • 示例 1:

    输入:1
    输出:10

    • 示例 2:

    输入:2
    输出:20

    • 示例 3:

    输入:3
    输出:46

    • 提示:

    1 <= N <= 5000

    思路1

    首先,我们来看下骑士在拨号盘上的走位
    骑士在拨号盘上的走位

    利用动态规划的思路,每一轮的结果都是由上一轮运算得到的(第一轮除外)
    第一轮推出第二轮

    算法实现

    • 无法通过OJ
    • 思路是正确的,限于数据类型,求取更大的数值会溢出
    class Solution {
        public int knightDialer(int N) {
            if (N == 1) return 10;
            
            // 第二轮 每个键位下一步数
            int[] jumpSizes = {2,2,2,2,3,0,3,2,2,2};
            // 临时存放 运算的值
            int[] tempSizes = new int[10];
            
            // 从底层开始 推理出顶层的答案
            for (int i = 2; i < N; i++) {
                giveNum(tempSizes,0, jumpSizes, 4, 6);
                giveNum(tempSizes,1, jumpSizes, 6, 8);
                giveNum(tempSizes,2, jumpSizes, 7, 9);
                giveNum(tempSizes,3, jumpSizes, 4, 8);
                giveNum(tempSizes,4, jumpSizes, 3, 9, 0);
                giveNum(tempSizes,6, jumpSizes, 1, 7, 0);
                giveNum(tempSizes,7, jumpSizes, 2, 6);
                giveNum(tempSizes,8, jumpSizes, 1, 3);
                giveNum(tempSizes,9, jumpSizes, 2, 4);
                tempSizes[5] = 0;
                
                // 更新当前轮次的结果
                jumpSizes = Arrays.copyOf(tempSizes, 10);
            }
            
            int sum = 0;
            for (int num : jumpSizes) sum += num;
            
            return sum;
        }
        
        void giveNum(int[] dest, int i, int[] src, int j, int k) {
            dest[i] = src[j] + src[k];
        }
        
        void giveNum(int[] dest, int i, int[] src, int j, int k, int p) {
            dest[i] = src[j] + src[k] + src[p];
        }
    }
    
    • 可以通过OJ
    • 过程一样,只是改变数据类型,加上取模
    class Solution {
        int mod = 1000000007;
        public int knightDialer(int N) {
            if (N == 1) return 10;
            
            // 将int改为long 数据不会溢出
            long[] jumpSizes = {2,2,2,2,3,0,3,2,2,2};
            long[] tempSizes = new long[10];
            long sum = 0;
            
            for (int i = 2; i < N; i++) {
                giveNum(tempSizes,0, jumpSizes, 4, 6);
                giveNum(tempSizes,1, jumpSizes, 6, 8);
                giveNum(tempSizes,2, jumpSizes, 7, 9);
                giveNum(tempSizes,3, jumpSizes, 4, 8);
                giveNum(tempSizes,4, jumpSizes, 3, 9, 0);
                giveNum(tempSizes,6, jumpSizes, 1, 7, 0);
                giveNum(tempSizes,7, jumpSizes, 2, 6);
                giveNum(tempSizes,8, jumpSizes, 1, 3);
                giveNum(tempSizes,9, jumpSizes, 2, 4);
                tempSizes[5] = 0;
                
                jumpSizes = Arrays.copyOf(tempSizes, 10);
            }
            
            for (long num : jumpSizes) sum += num;
            return (int)(sum%mod);
        }
        
        void giveNum(long[] dest, int i, long[] src, int j, int k) {
            // 每次求和后 都进行取模
            dest[i] = (src[j] + src[k]) % mod;
        }
        
        void giveNum(long[] dest, int i, long[] src, int j, int k, int p) {
            dest[i] = (src[j] + src[k] + src[p]) % mod;
        }
    }
    

    思路2

    通过观察各键位的位置以及其运算轨迹,总结出规律



    算法实现

    class Solution {
        public int knightDialer(int N) {
            if (N == 1) return 10;
            
            int mod = 1000000007;
            // a表示四角的和
            // b表示中部左右的和
            // c表示中部上下的和
            // d表示0键位的值
            long a = 4, b = 2, c = 2, d = 1;
            for (int i = 1; i < N; i++) {
                long A = (2*(b+c)) % mod;
                long B = (a + 2*d) % mod;
                c = a;
                d = b;
                a = A;
                b = B;
            }
            
            return (int)((a+b+c+d)%mod);
        }
    }
    
  • 相关阅读:
    数据库第三范式的思考
    channel通道例子
    go 测试代码性能实例
    go 新建項目引入gin失敗
    go 创建切片slice的四种方法
    Hibernate查询操作
    shell 分割训练数据
    hadoop streaming 分桶到不同的part
    C语言调用另一个文件的方法
    在springboot中使用jdbcTemplate(3)
  • 原文地址:https://www.cnblogs.com/slowbirdoflsh/p/11284996.html
Copyright © 2020-2023  润新知