Leetcode935 骑士拨号器

* @Description 问题为骑士跳 N 步有多少种跳法
* 我们以步数 N 分割问题,此时发现,影响问题解的不只是步数,还在于骑士当前处在什么位置
* 因为这决定了骑士下一步会不会越界
* 所以我们以骑士所处的位置 x,y 以及剩余跳的步数 n 来定义问题
* g(x,y,n) 表示处在 x,y 位置的骑士跳 n 步有多少种跳法
* 首先可以肯定的是,x,y,n 三个纬度可以唯一确定一个解,那么我们再来尝试推演状态转移关系
* 如果存在正确的状态转移关系,则该定义可用
* 明显的,状态转移关系为:
* g(x,y,n)=g(x-1,y-2,n-1)+g(x+1,y-2,n-1)+g(x+2,y-1,n-1)+g(x+2,y+1,n-1)+g(x+1,y+2,n-1)
* +g(x-1,y+2,n-1)+g(x-2,y+1,n-1)+g(x-2,y-1,n-1)
* 在号码盘上,我们要首先判断越界,再处理回归边界
* x>2 , y>3 ,(0,0),(2,0) 为越界情况
* n = 0 为 回归条件,回归 1
* 解空间为一颗 8 叉树(暂且这么叫吧),每一层的 8 个节点发散下去后,难免存在与同层其他节点发散到相同子问题的情况,
* 事实上该情况还比较多
* 那么建立缓存可以很好的帮助我们避免重复计算,或者换句话说,该问题定义方式帮我们找出了许多解空间中可重复利用的部分
* 对时间复杂度要求不是很极端的话,该思路应该可以通过

  BigInteger mod = new BigInteger("1000000007");

    public int knightDialer(int N) {
        if (N == 0) {
            return 0;
        }
        Map<String, BigInteger> cache = new HashMap<String, BigInteger>();
        BigInteger re = new BigInteger("0");
        for (int x = 0; x < 3; x++) {
            for (int y = 0; y < 4; y++) {
                re = re.add(g(x, y, N - 1, cache));
            }
        }
        return re.mod(mod).intValue();
    }

    public final BigInteger g(int x, int y, int n, Map<String, BigInteger> cache) {
        if (x < 0 || y < 0 || x > 2 || y > 3 || (x == 2 && y == 0) || (x == 0 && y == 0)) {
            return new BigInteger("0");
        }
        if (n == 0) {
            return new BigInteger("1");
        }
        String key = String.valueOf(x) + "|" + String.valueOf(y) + "|" + String.valueOf(n);
        if (cache.keySet().contains(key)) {
            return cache.get(key);
        }
        BigInteger re = new BigInteger("0");
        re = re.add(g(x - 1, y + 2, n - 1, cache));
        re = re.add(g(x + 1, y + 2, n - 1, cache));
        re = re.add(g(x + 2, y + 1, n - 1, cache));
        re = re.add(g(x + 2, y - 1, n - 1, cache));
        re = re.add(g(x + 1, y - 2, n - 1, cache));
        re = re.add(g(x - 1, y - 2, n - 1, cache));
        re = re.add(g(x - 2, y - 1, n - 1, cache));
        re = re.add(g(x - 2, y + 1, n - 1, cache));
        cache.put(key, re);
        return re;
    }

  优化为递推:

public final int knightDialer(int N) {
        int mod = 1000000007;
        long[] array = new long[10];
        Arrays.fill(array, 1);
        for(int n = 2; n <= N; n++) {
            long a1 = array[6] + array[8];
            long a2 = array[7] + array[9];
            long a3 = array[4] + array[8];
            long a4 = array[3] + array[9] + array[0];
            long a6 = array[1] + array[7] + array[0];
            long a7 = array[2] + array[6];
            long a8 = array[1] + array[3];
            long a9 = array[4] + array[2];
            long a0 = array[4] + array[6];
            array[0] = a0 % mod;
            array[1] = a1 % mod;
            array[2] = a2 % mod;
            array[3] = a3 % mod;
            array[4] = a4 % mod;
            array[5] = 0;
            array[6] = a6 % mod;
            array[7] = a7 % mod;
            array[8] = a8 % mod;
            array[9] = a9 % mod;
        }
        long sum = 0;
        for(long a : array) {
            sum += a;
        }
        return (int)(sum % mod);
    }

 

posted @ 2020-05-25 21:12  牛有肉  阅读(241)  评论(0编辑  收藏  举报