【LeetCode - 351】安卓系统手势解锁

1、题目描述

在这里插入图片描述

2、解题思路

  题目要求统计至少经过 m 个点,但是不超过 n 个点的所有解锁手势种类数量,因此,我们分别统计走过 m、走过 m+1、…、走过 n 个点的解锁手势种类,然后全部加起来就是至少经过 m 个点又不超过 n 个点的手势种类数。

  以下为统计走过 i 个点的手势种类数计算:

  我们从一个点出发,不断滑来滑去,直到总共划过 i 个点,就是一种解锁手势,现在要统计总共有多少种,很明显要进行 DFS,即深度优先搜索遍历。

  而且再仔细观察本题,会发现一个规律:从 1、3、7、9 出发的种类数是一样的,从 2、4、6、8 出发的种类数也是一样的。

  因此,我们只要计算从 1 出发的解锁种类数;然后乘以4,计算 从 2 出发的解锁种类数,然后乘以 4;最后加上从 5 出发的解锁种类数;三者加起来,就是经过 i 个点的手势种类数了。

  本题还有一个要求,即,如果两个点中间存在未走过的点,是不能走过去的。

  因为总共才 1-9 的点,我们用二维整型数组 skip[][] 来记录两点中间的点的情况,比如 skip[1][3] = 2,表示 1 和 3 中间有一个 2。

  再用一个一维布尔型数组 visited[] 来表示某个点是否走过,比如 visited[3] true 表示 3 已经走过。

  明白了上面的准备工作,我们就可以设计 DFS 函数了,DFS 函数的功能是:统计从某个点出发的所有不同的解锁手势的种类数。

  public int DFS(int current, boolean []visited, int [][]skip,int remainKeyCount);

  其中,current 表示出发点,remainKeyCount 表示走了 current 后的剩余可走步数。

  注意:在 DFS 函数的开始,要把 current 设置为 true,到 DFS 函数结束之前,要重新设置为 false。毕竟每一个点都是可以重复利用的。

3、解题代码

class Solution {
    public int numberOfPatterns(int m, int n) {
        int[][] skip = new int[10][10];
        //这个skip数组是为了记录跳跃的点数,比如说从1到3,就跳跃2
        //而且因为是对称的操作,所以3到1也是如此
        skip[1][3] = skip[3][1] = 2;
        skip[1][7] = skip[7][1] = 4;
        skip[3][9] = skip[9][3] = 6;
        skip[4][6] = skip[6][4] = skip[2][8] = skip[8][2] = 5;
        skip[1][9] = skip[9][1] = skip[3][7] = skip[7][3] = 5;
        skip[7][9] = skip[9][7] = 8;
        int result = 0;
        boolean[] visited = new boolean[10];
        //深度遍历所有情况,即走过的路程分别为 m,m+1,...,n-1,n 时的方法数
        for(int i = m; i<=n; i++){
            // 为什么 i 要减一呢,因为我们是从 i 出发,即出发也算走了一步
            //因为从1,3,7,9出发都是对称的
            result += DFS(1,visited,skip,i-1)*4;
            //2,4,6,8对称
            result += DFS(2,visited,skip,i-1)*4;
            //唯独5独立
            result += DFS(5,visited,skip,i-1);
        }
        return result;
    }

    /**
     *
     * @param current   起点
     * @param visited   保存所有点是否走过的布尔数组
     * @param skip      记录两点之间是否存在其他点的二维数组
     * @param remainKeyCount    剩余可走步数
     * @return
     */
    public int DFS(int current, boolean []visited, int [][]skip,int remainKeyCount){
        if(remainKeyCount == 0){
            return 1;
        }
        int result = 0; // 从这个点开始,往后走完 remainKeyCount 的所有可能情况数
        visited[current] = true;    // 当前点已经走过了
        // 遍历 current 的下一步所有的可能目标
        for(int i = 1; i <= 9; i++){
            // 看当前的节点到i节点的路径中有没有其他节点在中间
            int crossThroughNumber = skip[current][i];
            // 如果这一次我们的i节点没有被读过,如果读过,就忽略这个点
            // 如果没用过,看能不能走过去
            // 即判断有没有路过中间节点(visited[crossThroughNumber])或者这两个节点相邻没有中间节点(currentThrough=0)
            if(!visited[i] && (crossThroughNumber == 0 ||visited[crossThroughNumber])){
                // current 点已经处理好,剩下的路就交给 i 点了,已走了一步,因此 remainKeyCount-1
                result += DFS(i,visited,skip,remainKeyCount-1);
            }
        }
        visited[current] = false;   // 已经统计好 current 往下走的所有可能情况数,重置为 false
        return result;
    }
}
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值