题目描述
我们都知道安卓有个手势解锁的界面,是一个 3 x 3 的点所绘制出来的网格。
给你两个整数,分别为 m 和 n,其中 1 ≤ m ≤ n ≤ 9,那么请你统计一下有多少种解锁手势,是至少需要经过 m 个点,但是最多经过不超过 n 个点的。
先来了解下什么是一个有效的安卓解锁手势:
每一个解锁手势必须至少经过 m 个点、最多经过 n 个点。
解锁手势里不能设置经过重复的点。
假如手势中有两个点是顺序经过的,那么这两个点的手势轨迹之间是绝对不能跨过任何未被经过的点。
经过点的顺序不同则表示为不同的解锁手势。
解释:
| 1 | 2 | 3 |
| 4 | 5 | 6 |
| 7 | 8 | 9 |
无效手势:4 - 1 - 3 - 6
连接点 1 和点 3 时经过了未被连接过的 2 号点。
无效手势:4 - 1 - 9 - 2
连接点 1 和点 9 时经过了未被连接过的 5 号点。
有效手势:2 - 4 - 1 - 3 - 6
连接点 1 和点 3 是有效的,因为虽然它经过了点 2 ,但是点 2 在该手势中之前已经被连过了。
有效手势:6 - 5 - 4 - 1 - 9 - 2
连接点 1 和点 9 是有效的,因为虽然它经过了按键 5 ,但是点 5 在该手势中之前已经被连过了。
示例:
输入: m = 1,n = 1
输出: 9
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/android-unlock-patterns
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
DFS+分情况讨论
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];
//深度遍历,遍历每一个点到点的次数
for(int i = m; i<=n; i++){
//因为从1,3,7,9出发都是对称的,为什么i要减一呢,因为我们是从1出发,先天少了一个节点
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;
}
//深度遍历
public int DFS(int current, boolean []visited, int [][]skip,int remainKeyCount){
if(remainKeyCount == 0){
return 1;
}
int result = 0;
//深度遍历都是渣男,做了又要反悔找下家
visited[current] = true;
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])){
result += DFS(i,visited,skip,remainKeyCount-1);
}
}
//将状态重新刷新为未访问过
visited[current] = false;
return result;
}
}