351. 安卓系统手势解锁
我们都知道安卓有个手势解锁的界面,是一个 3 x 3
的点所绘制出来的网格。用户可以设置一个 “解锁模式” ,通过连接特定序列中的点,形成一系列彼此连接的线段,每个线段的端点都是序列中两个连续的点。如果满足以下两个条件,则 k
点序列是有效的解锁模式:
5
或 6
没有提前出现的情况下连接点 2
和 9
是有效的,因为从点 2
到点 9
的线没有穿过点 5
或 6
的中心。2
没有提前出现的情况下连接点 1
和 3
是无效的,因为从圆点 1
到圆点 3
的直线穿过圆点 2
的中心。以下是一些有效和无效解锁模式的示例:
[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
和 n
,那么请返回有多少种 不同且有效的解锁模式 ,是 至少 需要经过 m
个点,但是 不超过 n
个点的。
两个解锁模式 不同 需满足:经过的点不同或者经过点的顺序不同。
示例 1:
输入:m = 1, n = 1 输出:9
示例 2:
输入:m = 1, n = 2 输出:65
提示:
1 <= m, n <= 9
原站题解
golang 解法, 执行用时: 12 ms, 内存消耗: 1.8 MB, 提交时间: 2023-10-22 10:19:12
func cal(m,n int, u,i0,j0 uint)int { if 0==n { return 1 } o:=0 if m<=0 { o++ } var i,j uint for i = 0; i < 3; i++ { for j = 0; j < 3; j++ { i1,j1,u1 := i0 + i, j0 + j, u | (1 << (i * 3 + j)); if u1 > u && (i1&1==1 || j1&1==1 || u1 & (1 << (i1 / 2 * 3 + j1 / 2))!=0) { o += cal(m - 1, n - 1, u1, i, j); } } } return o } func numberOfPatterns(m int, n int) int { return cal(m, n, 0, 1, 1) }
golang 解法, 执行用时: 60 ms, 内存消耗: 1.8 MB, 提交时间: 2023-10-22 10:18:59
func numberOfPatterns(m int, n int) int { uf := NewUnionFindSet(10) used := make([]bool, 10) ans := 0 uf.Mix(1, 3) uf.Mix(1, 9) uf.Mix(1, 7) uf.Mix(4, 6) uf.Mix(2, 8) var dfs func(currentPoint int, curCnt int) dfs = func(currentPoint int, curCnt int) { if curCnt >= m && curCnt <= n { ans++ } else if curCnt > n { return } used[currentPoint ] = true for i := 1; i <= 9; i++ { needCross := uf.connected(i, currentPoint ) if !used[i] && (!needCross || used[(currentPoint + i) / 2]) { dfs(i, curCnt + 1) } } used[currentPoint ] = false } for i := 1; i < 10; i++ { dfs(i, 1) } return ans } // UnionFindSet 并查集结构体 type UnionFindSet struct { id []int // 分量id sz []int //由触点索引的各个根节点对应的分量大小 count int // 连通分量数量 } //初始化分量id数组 func NewUnionFindSet(n int) *UnionFindSet { id := make([]int, n) for i := range id { id[i] = i } sz := make([]int, n) for i := range sz { sz[i] = i } return &UnionFindSet{ id: id, sz : sz, count: n, } } func (u *UnionFindSet) Find(p int) int { for p != u.id[p] { p = u.id[p] } return p } func (u *UnionFindSet) Mix(p, q int) { i := u.Find(p) j := u.Find(q) if i == j { return } if u.sz[i] < u.sz[j] { u.id[i] = j u.sz[j] += u.sz[i] } else { u.id[j] = i u.sz[i] += u.sz[j] } u.count-- } func (u *UnionFindSet) connected(p, q int) bool { return u.Find(p) == u.Find(q) }
cpp 解法, 执行用时: 32 ms, 内存消耗: 6.4 MB, 提交时间: 2023-10-22 10:18:40
class Solution { public: vector<int> dp; vector<int> check = vector<int>(9, 0); int ans = 0; void dfs(int step, int m, int n, int x, int y) { if (step >= m && step <= n) { ans++; } if (step == n) { return; } for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if (x == i && y == j) continue; if (check[i*3+j]) continue; if (abs(x-i) % 2 == 0 && abs(y-j) % 2 == 0 && !check[(x+i)/2*3+(y+j)/2]) continue; check[i*3+j] = 1; dfs(step+1, m, n, i, j); check[i*3+j] = 0; } } } int numberOfPatterns(int m, int n) { for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { check[i*3+j] = 1; dfs(1, m, n, i, j); check[i*3+j] = 0; } } return ans; } };
java 解法, 执行用时: 8 ms, 内存消耗: 39.9 MB, 提交时间: 2023-10-22 10:18:13
class Solution { public int numberOfPatterns(int m, int n) { int[][][] dp = new int[n][9][1<<9]; for(int j=0; j<9; j++) dp[0][j][1<<j] = 1; for(int i=1; i<n; i++){ for(int j=0; j<9; j++){ for(int k=0; k<9; k++){ for(int x=0; x<(1<<9); x++){ if(dp[i-1][k][x]==0 || (x & (1<<j) )!=0 ) continue; if(connect(x, k, j)) dp[i][j][x | (1<<j)] += dp[i-1][k][x]; } } } } int ans = 0; for(int i=m-1; i<n; i++){ for(int j=0; j<9; j++){ for(int x=0; x<(1<<9); x++) ans += dp[i][j][x]; } } return ans; } int[][] dict = new int[9][9]; { // 横 dict[0][2] = dict[2][0] = (1<<1); dict[3][5] = dict[5][3] = (1<<4); dict[6][8] = dict[8][6] = (1<<7); // 竖 dict[0][6] = dict[6][0] = (1<<3); dict[1][7] = dict[7][1] = (1<<4); dict[2][8] = dict[8][2] = (1<<5); // 对角 dict[0][8] = dict[8][0] = (1<<4); dict[2][6] = dict[6][2] = (1<<4); } boolean connect(int x, int k, int j){ return k!=j && (dict[k][j]==0 || (x&dict[k][j])!=0); } }
python3 解法, 执行用时: 52 ms, 内存消耗: 17.2 MB, 提交时间: 2023-10-22 10:17:20
from functools import lru_cache class Solution: def numberOfPatterns(self, m: int, n: int) -> int: graph = { 1: {3: 2, 7: 4, 9: 5}, 2: {8: 5}, 3: {1: 2, 7: 5, 9: 6}, 4: {6: 5}, 5: {}, 6: {4: 5}, 7: {1: 4, 3: 5, 9: 8}, 8: {2: 5}, 9: {1: 5, 3: 6, 7: 8}, } ans = 0 @lru_cache(None) def dfs(status, current, count): if count == n: return 1 current_ans = 0 if count < m else 1 for i in range(1, 10): if status & (1 << i) == 0: if i not in graph[current] or ((1 << graph[current][i]) & status): current_ans += dfs(status | (1 << i), i, count + 1) return current_ans # for cur in range(1, 10): # ans += dfs(1 << cur, cur, 1) ans += 4 * dfs(1 << 1, 1, 1) ans += 4 * dfs(1 << 2, 2, 1) ans += dfs(1 << 5, 5, 1) return ans
java 解法, 执行用时: 39 ms, 内存消耗: 38.2 MB, 提交时间: 2023-10-22 10:17:03
public class Solution { private boolean used[] = new boolean[9]; public int numberOfPatterns(int m, int n) { int res = 0; for (int len = m; len <= n; len++) { res += calcPatterns(-1, len); for (int i = 0; i < 9; i++) { used[i] = false; } } return res; } private boolean isValid(int index, int last) { if (used[index]) return false; // first digit of the pattern if (last == -1) return true; // knight moves or adjacent cells (in a row or in a column) if ((index + last) % 2 == 1) return true; // indexes are at both end of the diagonals for example 0,0, and 8,8 int mid = (index + last)/2; if (mid == 4) return used[mid]; // adjacent cells on diagonal - for example 0,0 and 1,0 or 2,0 and //1,1 if ((index%3 != last%3) && (index/3 != last/3)) { return true; } // all other cells which are not adjacent return used[mid]; } private int calcPatterns(int last, int len) { if (len == 0) return 1; int sum = 0; for (int i = 0; i < 9; i++) { if (isValid(i, last)) { used[i] = true; sum += calcPatterns(i, len - 1); used[i] = false; } } return sum; } }