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 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 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 getNextNumber(int number){
        List 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;
    }

你可能感兴趣的:(935. 骑士拨号器)