晚饭时和同事聊到安卓屏幕解锁时会有多少种解锁方案,觉得很有趣,吃完饭开始想办法解题,花了大概2个小时解决。思路如下:
- 使用索引值0-9表示从左到右、从上到下的9个点,行、列号很容易从索引值得到;
- 使用一个列表(routeList)来表示解锁路径,列表的元素为点的索引值;
- 从任意点N出发(将index(N)放入routeList),判断与哪些点(集合NextNodes)可以构成合法路径;然后用递归的方式从NextNodes中的点出发,寻找下一个可以构成合法路径的点;直到再也无法找到可用的点供路径使用
下一步,就是在Nexus 7上捣鼓,发现Android定义合法的解锁路径必须满足下列条件:
- 构成路径的点不得少于4个
- 路径中不能有重复的点
- (这点有点绕口)构成边或对角线的两点(例如[0,2]构成边,[0,8]构成对角线),不能在路径中相邻,除非其中点已经在路径中出现
接下来就是写代码了,Let's talk with the code!
问题的解是:389112
1 ############################################################### 2 ####### AndrUlkComb.py ######### 3 ## Calculate the COMBination count for ANDroid UNLock screen ## 4 ## By grepall@gmail.com ## 5 ############################################################### 6 7 8 ############################################################### 9 ## The Android unlock screen looks like below 10 ## ------------------------------- 11 ## o -> o -> o 12 ## 13 ## o o o 14 ## 15 ## o o o 16 ## -------------------------------- 17 ## 18 ## We'll use: 19 ## 1. Index from 0-8 to represent all 9 nodes instead of 20 ## using row:column format. 21 ## 2. An array (routeList in the code) to record the path of the unlock combination. 22 ## For example, for the unlock comb which is shown in 23 ## in the figure. There will be 3 elements in the array: 24 ## 0, 1, and 2. (Although it's NOT a valid path because of 25 ## not long enough) 26 ## 27 ## Valid unlock path must holds true for 3 conditions: 28 ## C1: It must pass 4 different nodes at least; 29 ## C2: It cannot contains duplicate nodes; 30 ## C3: This is a little tricky. Any node cannot direct link to another node, when 31 ## any node will be past through if we connect those 2 nodes in the unlock panel. UNLESS 32 ## the crossed node already EXISTs in the path. 33 ## For example, path (0, 2, 5, 4) is not valid, because 0-->2 crosses node 1. But path 34 ## (1, 0, 2, 5) is a vliad path. 35 ## This rule holds for diagonal as well. 36 37 38 39 ####################################################################### 40 # M stands for the width of the unlock panel 41 # H stands for the height of the unlock panel 42 # CAUTION: Modify this value will NOT generate correct result!!! That's TO BE DONE 43 # 44 W = 3 #Width 45 H = 3 #Height 46 47 # Unlock path 48 routeList = list() 49 50 # Combination count, which is we want to know 51 routeCount = 0 52 53 54 55 def isValidRoute(p1, route): 56 # If the candidate node exists in the unlock path 57 if route.count(p1) != 0: 58 return False 59 return True; 60 61 62 def isCrossPoint(p1, p2): 63 # Will the connection line (p1, p2) cross another node? 64 x1 = p1%W 65 y1 = p1/W 66 x2 = p2%W 67 y2 = p2/W 68 if x1 == x2 and abs(y1-y2) > 1: 69 return True # same column 70 if y1 == y2 and abs(x1-x2) > 1: 71 return True # same row 72 if abs(y1-y2) == abs(x1-x2) and abs(y1-y2)>1: 73 return True # diagonal 74 return False 75 76 def tryPoint(lastPt, route): 77 # Try next valid node for the unlock path recursively 78 global routeCount 79 for pt in range(0, W*H): 80 if isValidRoute(pt, route): #C2 81 crossPt = (lastPt+pt)/2 82 if isCrossPoint(lastPt, pt) and route.count(crossPt) == 0: #C3 83 continue 84 route.append(pt) 85 if len(route) >3: #C1 86 routeCount += 1 87 tryPoint(pt, route) 88 route.pop() 89 90 91 # Each node may be the start node for unlock 92 for i in range(0, W*H): 93 routeList = [i] 94 tryPoint(i, routeList) 95 print "Start from point %d, combination count is %d now..." % (i, routeCount) 96 print "Toatl combination counter for Android unlock screen: " + str(routeCount)