935. 骑士拨号器

骑士拨号器

1.想法:

根据题意:可得1下一步可以到达的地方为6,8


image.png

2可到的地方是7,9


image.png

依次类推,可以得到其他的到达序列,
因为第一次到达的是所有点,接下来的过程只是多次重复这个过程。那么我们记录下分别前一次到达的位置的次数,那么下一次所有的可能都是可知的

1.存储位置法:

也就是我们维护一个List,List里面存储的是所有可能到达的节点位置号,初始化为0,1,2,3,4,5,6,7,8,9.然后根据前一次跳到的位置,寻找下一次跳到的位置,例如前一次的0可以跳到4,6,那么下一个List就加上4和6,依次类推,直到坐上N-1次

class Solution {
     List<Integer> nextlist=new ArrayList<>();
     int count=0;
    static final int INDEX=(int) (Math.pow(10, 9)+7);
    public int knightDialer(int N) {
        if(N==1) {
            return 10;
        }
        
        for(int i=0;i<10;i++) {
            nextlist.add(i);
        }
        
        while(N-->1) {
            getMoveCount(nextlist,N);
        }
        return count%INDEX;
        
    }
    private void getMoveCount(List<Integer> movelist, int n) {
        int[] data=new int[] {2,2,2,2,3,0,3,2,2,2};
        //最后一步跳了
        if(n==1) {
            for(int item:movelist) {
                count+=data[item];
            }
        }else {//不是最后一步,寻找下一步的位置
            nextlist=new ArrayList<>();
            for(int item:movelist) {
                for(int next:getNextNumber(item)) {
                    nextlist.add(next);
                }
            }
        }
        
    }
    public List<Integer> getNextNumber(int number){
        List<Integer> list=new ArrayList<>();
        switch (number) {
        case 1:
            list.add(6);
            list.add(8);
            break;
        case 2:
            list.add(7);
            list.add(9);
            break;
        case 3:
            list.add(4);
            list.add(8);
            break;
        case 4:
            list.add(3);
            list.add(9);
            list.add(0);
            break;
        case 5:
            break;
        case 6:
            list.add(1);
            list.add(7);
            list.add(0);
            break;
        case 7:
            list.add(2);
            list.add(6);
            break;
        case 8:
            list.add(1);
            list.add(3);
            break;
        case 9:
            list.add(4);
            list.add(2);
            break;
        case 0:
            list.add(4);
            list.add(6);
            break;
        }
        return list;
        
    }
}

这个方法的复杂度很高,当N=17就超时了


image.png

方法二:利用递归

我们第N次的结果就是第N-1次的结果的再处理
例如我们f(0,n-1) = f(4,n-1)+f(6,n-1)
那么可得

/*
* 用来返回n次的,k的次数
*/
private int getSum(int i, int n) {
        
        if(n==1)return 1;
        switch (i){
            case 0:
                return getSum(4,n-1)%mod+getSum(6,n-1)%mod;
            case 1:
                return getSum(6,n-1)%mod+getSum(8,n-1)%mod;
            case 2:
                return getSum(7,n-1)%mod+getSum(9,n-1)%mod;
            case 3:
                return getSum(4,n-1)%mod+getSum(8,n-1)%mod;
            case 4:
                return getSum(3,n-1)%mod+getSum(9,n-1)%mod+getSum(0,n-1)%mod;
            case 5:
                return 0;
            case 6:
                return getSum(1,n-1)%mod+getSum(7,n-1)%mod+getSum(0,n-1)%mod;
            case 7:
                return getSum(2,n-1)%mod+getSum(6,n-1)%mod;
            case 8:
                return getSum(1,n-1)%mod+getSum(3,n-1)%mod;
            case 9:
                return getSum(4,n-1)%mod+getSum(2,n-1)%mod;

        }
        return -1;
    }

最后加起来:

 public int knightDialer(int N) {
        int sum =0;
        for(int i = 0;i<10;i++){
            sum=(sum+getSum(i,N))%mod;
        }
        return sum;
    }

结果在161超时


image.png

3.数组存储法

就是int[] res = new int[10];代表了现在每个位置的个数
例如res[0]代表了现在在0位置的个数
我们可以预测在下一步的res[0]个数
res[0] = res[4] + res[6];但是不能这样写,因为这样就污染了数据
所以new一个新的数组temp用来存储下一次的数据
所以下一次的数据为:

int[] temp = new int[res.length];
            temp[0] = (res[4] + res[6])%mod;
            temp[1] = (res[6] + res[8])%mod;
            temp[2] = (res[7] + res[9])%mod;
            temp[3] = (res[4] + res[8])%mod;
            temp[4] = (res[0] + res[3] + res[9])%mod;
            temp[5] = 0;
            temp[6] = (res[0] + res[1] + res[7])%mod;
            temp[7] = (res[2] + res[6])%mod;
            temp[8] = (res[1] + res[3])%mod;
            temp[9] = (res[2] + res[4])%mod;
            for(int j=0;j<10;j++){
                res[j] = temp[j]%mod;
            }

既然一次做成功了,只需坐上N-1次

public int knightDialer(int N) {
        int[] res = new int[10];
        for (int i = 0; i < 10; i++) {
            res[i] = 1;
        }
        for (int i = 2; i <= N; i++) {
            int[] temp = new int[res.length];
            temp[0] = (res[4] + res[6])%mod;
            temp[1] = (res[6] + res[8])%mod;
            temp[2] = (res[7] + res[9])%mod;
            temp[3] = (res[4] + res[8])%mod;
            temp[4] = (res[0] + res[3] + res[9])%mod;
            temp[5] = 0;
            temp[6] = (res[0] + res[1] + res[7])%mod;
            temp[7] = (res[2] + res[6])%mod;
            temp[8] = (res[1] + res[3])%mod;
            temp[9] = (res[2] + res[4])%mod;
            for(int j=0;j<10;j++){
                res[j] = temp[j]%mod;
            }
        }
        int sum=0;
        for(int i=0;i<10;i++){
            System.out.println(res[i]);
        }
        return sum;

但是这个是存在问题的,因为两个数x相加不会超过Integer.MAX_VALUE
三个数会。(因为每个数都取10^9+7的余数,保证2个数相加不会越界)
所以修改所有三个数相加的部分

public int knightDialer(int N) {
        int[] res = new int[10];
        for (int i = 0; i < 10; i++) {
            res[i] = 1;
        }
        for (int i = 2; i <= N; i++) {
            int[] temp = new int[res.length];
            temp[0] = (res[4] + res[6])%mod;
            temp[1] = (res[6] + res[8])%mod;
            temp[2] = (res[7] + res[9])%mod;
            temp[3] = (res[4] + res[8])%mod;
            temp[4] = ((res[0] + res[3])%mod + res[9])%mod;
            temp[5] = 0;
            temp[6] = ((res[0] + res[1])%mod + res[7])%mod;
            temp[7] = (res[2] + res[6])%mod;
            temp[8] = (res[1] + res[3])%mod;
            temp[9] = (res[2] + res[4])%mod;
            for(int j=0;j<10;j++){
                res[j] = temp[j]%mod;
            }
        }
        int sum=0;
        for(int i=0;i<10;i++){
            System.out.println(res[i]);
        }
        return sum;
    }

OK,编译通过

其实我们每次都不必要进行new一个数组,因为这样的控件开销为O(N),且省去了复制的过程
我们利用二维数组
dp[k][i] 表示k位置的第i次移动所得到的次数

例如dp[0][n] = dp[4][n-1]+dp[6][n-1]

2.代码:

1.利用二维数组,(动态规划)

 public int knightDialer(int N) {
        int[][] dp = new int[10][N+1];
        for (int i = 0; i < 10; i++) {
            dp[i][1]=1;
        }
        for (int i = 2; i <= N; i++) {
            dp[0][i] = (dp[4][i-1]+dp[6][i-1])%mod;
            dp[1][i] = (dp[6][i-1]+dp[8][i-1])%mod;
            dp[2][i] = (dp[7][i-1]+dp[9][i-1])%mod;
            dp[3][i] = (dp[4][i-1]+dp[8][i-1])%mod;
            dp[4][i] = ((dp[0][i-1]+dp[3][i-1])%mod+dp[9][i-1])%mod;
            dp[5][i] = 0;
            dp[6][i] = ((dp[0][i-1]+dp[1][i-1])%mod+dp[7][i-1])%mod;
            dp[7][i] = (dp[2][i-1]+dp[6][i-1])%mod;
            dp[8][i] = (dp[1][i-1]+dp[3][i-1])%mod;
            dp[9][i] = (dp[2][i-1]+dp[4][i-1])%mod;

        }
        int sum=0;
        for(int i=0;i<10;i++){
            sum=sum+dp[i][N];
            sum=sum%mod;
        }
        return sum;
    }

2.利用数组记录

 public int knightDialer(int N) {
        int[] res = new int[10];
        for (int i = 0; i < 10; i++) {
            res[i] = 1;
        }
        for (int i = 2; i <= N; i++) {
            int[] temp = new int[res.length];
            temp[0] = (res[4] + res[6])%mod;
            temp[1] = (res[6] + res[8])%mod;
            temp[2] = (res[7] + res[9])%mod;
            temp[3] = (res[4] + res[8])%mod;
            temp[4] = ((res[0] + res[3])%mod + res[9])%mod;
            temp[5] = 0;
            temp[6] = ((res[0] + res[1])%mod + res[7])%mod;
            temp[7] = (res[2] + res[6])%mod;
            temp[8] = (res[1] + res[3])%mod;
            temp[9] = (res[2] + res[4])%mod;
            for(int j=0;j<10;j++){
                res[j] = temp[j]%mod;
            }
        }
        int sum=0;
        for(int i=0;i<10;i++){
              sum=sum+res[i];
              if(sum >= mod)sum= sum%mod;
        }
        return sum;
    }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,142评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,298评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,068评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,081评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,099评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,071评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,990评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,832评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,274评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,488评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,649评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,378评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,979评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,625评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,643评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,545评论 2 352

推荐阅读更多精彩内容

  • 在C语言中,五种基本数据类型存储空间长度的排列顺序是: A)char B)char=int<=float C)ch...
    夏天再来阅读 3,340评论 0 2
  • 分治方法 将问题划分成互不相交的子问题 递归地求解子问题 将子问题的解组合起来 动态规划(两个要素:最优子结构、子...
    superlj666阅读 498评论 0 0
  • 动态规划(Dynamic Programming) 本文包括: 动态规划定义 状态转移方程 动态规划算法步骤 最长...
    廖少少阅读 3,279评论 0 18
  • 概要 64学时 3.5学分 章节安排 电子商务网站概况 HTML5+CSS3 JavaScript Node 电子...
    阿啊阿吖丁阅读 9,180评论 0 3
  • (欢迎转载,但请注明出处并附带链接)算法好久没复习了,今天看见一妹子在办公室刷Leetcode,顿时我也来了兴趣,...
    Nick_Zuo阅读 663评论 0 3