题目原文:
LCP 27. 黑盒光线反射
秋日市集上有个奇怪的黑盒,黑盒的主视图为 n*m 的矩形。从黑盒的主视图来看,黑盒的上面和下面各均匀分布有 m 个小孔,黑盒的左面和右面各均匀分布有 n 个小孔。黑盒左上角小孔序号为 0,按顺时针编号,总共有 2*(m+n) 个小孔。每个小孔均可以打开或者关闭,初始时,所有小孔均处于关闭状态。每个小孔上的盖子均为镜面材质。例如一个 2*3 的黑盒主视图与其小孔分布如图所示:
店长告诉小扣,这里是「几何学的快问快答」,店长可能有两种操作:
open(int index, int direction)
- 若小孔处于关闭状态,则打开小孔,照入光线;否则直接照入光线;close(int index)
- 关闭处于打开状态小孔,店长保证不会关闭已处于关闭状态的小孔;
其中:
index
: 表示小孔序号direction
:1
表示光线沿 y=xy=xy=x 方向,-1
表示光线沿 y=−xy=-xy=−x 方向。
当光线照至边界时:若边界上的小孔为开启状态,则光线会射出;否则,光线会在小孔之间进行反射。特别地:
- 若光线射向未打开的拐角(黑盒顶点),则光线会原路反射回去;
- 光线自拐角处的小孔照入时,只有一种入射方向(如自序号为 0 的小孔照入方向只能为
-1
)
请帮助小扣判断并返回店长每次照入的光线从几号小孔射出。
示例 1:
输入:
["BlackBox","open","open","open","close","open"]
[[2,3],[6,-1],[4,-1],[0,-1],[6],[0,-1]]
输出:
[null,6,4,6,null,4]
解释:
BlackBox b = BlackBox(2,3); // 新建一个 2x3 的黑盒
b.open(6,-1) // 打开 6 号小孔,并沿 y=-x 方向照入光线,光线至 0 号小孔反射,从 6 号小孔射出
b.open(4,-1) // 打开 4 号小孔,并沿 y=-x 方向照入光线,光线轨迹为 4-2-8-2-4,从 4 号小孔射出
b.open(0,-1) // 打开 0 号小孔,并沿 y=-x 方向照入光线,由于 6 号小孔为开启状态,光线从 6 号小孔射出
b.close(6) // 关闭 6 号小孔
b.shoot(0,-1) // 从 0 号小孔沿 y=-x 方向照入光线,由于 6 号小孔为关闭状态,4 号小孔为开启状态,光线轨迹为 0-6-4,从 4 号小孔射出
示例 2:
输入:
["BlackBox","open","open","open","open","close","open","close","open"]
[[3,3],[1,-1],[5,1],[11,-1],[11,1],[1],[11,1],[5],[11,-1]]
输出:
[null,1,1,5,1,null,5,null,11]
解释:
BlackBox b = BlackBox(3,3); // 新建一个 3x3 的黑盒
b.open(1,-1) // 打开 1 号小孔,并沿 y=-x 方向照入光线,光线轨迹为 1-5-7-11-1,从 1 号小孔射出
b.open(5,1) // 打开 5 号小孔,并沿 y=x 方向照入光线,光线轨迹为 5-7-11-1,从 1 号小孔射出
b.open(11,-1) // 打开 11 号小孔,并沿逆 y=-x 方向照入光线,光线轨迹为 11-7-5,从 5 号小孔射出
b.open(11,1) // 从 11 号小孔沿 y=x 方向照入光线,光线轨迹为 11-1,从 1 号小孔射出
b.close(1) // 关闭 1 号小孔
b.open(11,1) // 从 11 号小孔沿 y=x 方向照入光线,光线轨迹为 11-1-5,从 5 号小孔射出
b.close(5) // 关闭 5 号小孔
b.open(11,-1) // 从 11 号小孔沿 y=-x 方向照入光线,光线轨迹为 11-1-5-7-11,从 11 号小孔射出
提示:
1 <= n, m <= 10000
1 <= 操作次数 <= 10000
direction
仅为1
或-1
0 <= index < 2*(m+n)
尝试解答:
先上我自己的代码:
1 class BlackBox(object): 2 3 def __init__(self, n, m): 4 """ 5 :type n: int 6 :type m: int 7 """ 8 self.m = m 9 self.n = n 10 self.pt_list = self.mkpoints() 11 12 13 def open(self, index, direction): 14 """ 15 :type index: int 16 :type direction: int 17 :rtype: int 18 """ 19 20 self.pt_list[index]["state"] = "open" 21 list_open = [] 22 list_close = [] 23 list_conner = [] 24 for dic in self.pt_list: 25 if dic["state"] == "open": 26 list_open.append(dic["coord"]) 27 if dic["state"] == "close": 28 list_close.append(dic["coord"]) 29 if dic["location"] == "top_left" or dic["location"] == "top_right" or dic["location"] == "bottom_left" or dic["location"] == "bottom_right": 30 list_conner.append(dic["coord"]) 31 for dic in self.pt_list: 32 if dic["id"] == index: 33 coord = dic["coord"] 34 direction = direction 35 out_coord = self.guangxian(coord,self.pt_list,direction,list_open,list_close,list_conner) 36 37 for dic in self.pt_list: 38 if dic["coord"] == out_coord: 39 40 return dic["id"] 41 42 def close(self, index): 43 """ 44 :type index: int'method' object is not subscriptable 45 :rtype: None 46 """ 47 self.pt_list[index]["state"] = "close" 48 49 return None 50 51 52 53 def mkpoints(self): 54 n = self.n 55 m = self.m 56 sum = (n+m)*2 57 pt_list = [] #创建一个列表用来存储每个孔的字典 58 for i in range(sum): #为每个孔创建字典 59 pt_list.append(dict(id=i,state="close",coord=[0,0],location="top_left")) 60 for dic in pt_list: #初始化每个孔的字典 61 if dic["id"] == 0: 62 dic["location"] = "top_left" 63 dic["coord"] = [0,0] 64 if dic["id"] == m: 65 dic["location"] = "top_right" 66 dic["coord"] = [0,m] 67 if dic["id"] == m+n: 68 dic["location"] = "bottom_right" 69 dic["coord"] = [n,m] 70 if dic["id"] == 2*m+n: 71 dic["location"] = "bottom_left" 72 dic["coord"] = [n,0] 73 if dic["id"] > 0 and dic["id"] < m: 74 dic["location"] = "top" 75 dic["coord"] = [0,dic["id"]] 76 if dic["id"] > m and dic["id"] < (m+n): 77 dic["location"] = "right" 78 dic["coord"] = [dic["id"]-m,m] 79 if dic["id"] > (m+n) and dic["id"] < (2*m+n): 80 dic["location"] = "bottom" 81 dic["coord"] = [n,(2*m+n)-dic["id"]] 82 if dic["id"] > (2*m+n) and dic["id"] < sum: 83 dic["location"] = "left" 84 dic["coord"] = [sum-dic["id"],0] 85 86 return pt_list 87 88 89 90 def guangxian(self,coord,pt_list,direction,list_open,list_close,list_conner): 91 for dic in pt_list: 92 if dic["coord"] == coord: 93 index = dic["id"] 94 location = dic["location"] 95 if direction == 1: 96 if location == "top": 97 while(True): 98 coord = [coord[0]+1,coord[1]-1] 99 #print(coord) 100 if coord in list_open: 101 return coord 102 if coord in list_close: 103 if coord in list_conner: 104 return self.guangxian(coord,pt_list,direction,list_open,list_close,list_conner) 105 else: 106 return self.guangxian(coord,pt_list,direction*(-1),list_open,list_close,list_conner) 107 if location == "bottom": 108 while(True): 109 coord = [coord[0]-1,coord[1]+1] 110 #print(coord) 111 if coord in list_open: 112 return coord 113 if coord in list_close: 114 if coord in list_conner: 115 return self.guangxian(coord,pt_list,direction,list_open,list_close,list_conner) 116 else: 117 return self.guangxian(coord,pt_list,direction*(-1),list_open,list_close,list_conner) 118 if location == "left": 119 while(True): 120 coord = [coord[0]-1,coord[1]+1] 121 #print(coord) 122 if coord in list_open: 123 return coord 124 if coord in list_close: 125 if coord in list_conner: 126 return self.guangxian(coord,pt_list,direction,list_open,list_close,list_conner) 127 else: 128 return self.guangxian(coord,pt_list,direction*(-1),list_open,list_close,list_conner) 129 if location == "right": 130 while(True): 131 coord = [coord[0]+1,coord[1]-1] 132 #print(coord) 133 if coord in list_open: 134 return coord 135 if coord in list_close: 136 if coord in list_conner: 137 return self.guangxian(coord,pt_list,direction,list_open,list_close,list_conner) 138 else: 139 return self.guangxian(coord,pt_list,direction*(-1),list_open,list_close,list_conner) 140 if location == "top_right": 141 while(True): 142 coord = [coord[0]+1,coord[1]-1] 143 #print(coord) 144 if coord in list_open: 145 return coord 146 if coord in list_close: 147 if coord in list_conner: 148 return self.guangxian(coord,pt_list,direction,list_open,list_close,list_conner) 149 else: 150 return self.guangxian(coord,pt_list,direction*(-1),list_open,list_close,list_conner) 151 if location == "bottom_left": 152 while(True): 153 coord = [coord[0]-1,coord[1]+1] 154 #print(coord) 155 if coord in list_open: 156 return coord 157 if coord in list_close: 158 if coord in list_conner: 159 return self.guangxian(coord,pt_list,direction,list_open,list_close,list_conner) 160 else: 161 return self.guangxian(coord,pt_list,direction*(-1),list_open,list_close,list_conner) 162 if direction == -1: 163 if location == "top": 164 while(True): 165 coord = [coord[0]+1,coord[1]+1] 166 #print(coord) 167 if coord in list_open: 168 return coord 169 if coord in list_close: 170 if coord in list_conner: 171 return self.guangxian(coord,pt_list,direction,list_open,list_close,list_conner) 172 else: 173 return self.guangxian(coord,pt_list,direction*(-1),list_open,list_close,list_conner) 174 if location == "bottom": 175 while(True): 176 coord = [coord[0]-1,coord[1]-1] 177 #print(coord) 178 if coord in list_open: 179 return coord 180 if coord in list_close: 181 if coord in list_conner: 182 return self.guangxian(coord,pt_list,direction,list_open,list_close,list_conner) 183 else: 184 return self.guangxian(coord,pt_list,direction*(-1),list_open,list_close,list_conner) 185 if location == "left": 186 while(True): 187 coord = [coord[0]+1,coord[1]+1] 188 #print(coord) 189 if coord in list_open: 190 return coord 191 if coord in list_close: 192 if coord in list_conner: 193 return self.guangxian(coord,pt_list,direction,list_open,list_close,list_conner) 194 else: 195 return self.guangxian(coord,pt_list,direction*(-1),list_open,list_close,list_conner) 196 if location == "right": 197 while(True): 198 coord = [coord[0]-1,coord[1]-1] 199 #print(coord) 200 if coord in list_open: 201 return coord 202 if coord in list_close: 203 if coord in list_conner: 204 return self.guangxian(coord,pt_list,direction,list_open,list_close,list_conner) 205 else: 206 return self.guangxian(coord,pt_list,direction*(-1),list_open,list_close,list_conner) 207 if location == "top_left": 208 while(True): 209 coord = [coord[0]+1,coord[1]+1] 210 #print(coord) 211 if coord in list_open: 212 return coord 213 if coord in list_close: 214 if coord in list_conner: 215 return self.guangxian(coord,pt_list,direction,list_open,list_close,list_conner) 216 else: 217 return self.guangxian(coord,pt_list,direction*(-1),list_open,list_close,list_conner) 218 if location == "bottom_right": 219 while(True): 220 coord = [coord[0]-1,coord[1]-1] 221 #print(coord) 222 if coord in list_open: 223 return coord 224 if coord in list_close: 225 if coord in list_conner: 226 return self.guangxian(coord,pt_list,direction,list_open,list_close,list_conner) 227 else: 228 return self.guangxian(coord,pt_list,direction*(-1),list_open,list_close,list_conner) 229 230 231 # Your BlackBox object will be instantiated and called as such: 232 # obj = BlackBox(n, m) 233 # param_1 = obj.open(index,direction) 234 # obj.close(index)
思路十分简单无脑(意料之中的,运行短例子正确,运行超长例子时超时了),算法的设计非常欠考虑,也带来了相应的代价,导致编码时间过长、bug出现的可能性增加。黑盒被定义后,首先确定每个孔的状态,将每个孔的状态保存在一个字典中再将所有字典保存在一串列表中,其中小孔的状态包括index、开闭状态、在盒子中的方位(上下左右四角)、坐标,每次小孔的状态变化,都会先查询这个列表,在字典上进行修改,这其实是一个很大的问题,造成很高的空间复杂度,因此我的代码无法在更大的“盒子”上运行,之后就是常规的递归算法,使guangxian在盒子的孔之间传播,每传播一次检验一次孔的状态,并判断下一个传播方向。依次递归。
这里因为python语法基础掌握不牢犯了三个低级错误:
一、类函数自我递归的时候没有在函数开头加self.,这里应该记住类函数自我递归的时候算作定义类内的同类间函数调用,因此应该在函数开头加self.,而不应该在参数中加self(例:function(self,x),只有在类内定义函数的时候才会这么写)。
二、递归的函数忘记return(好蠢),导致递归过程根本停不下来,浪费了许多时间来找问题所在。
三、忘记定义类内部静态变量,导致pt_list无法常驻,只存在函数中的内部变量无法实现某些特定功能,下次记住。
标准题解:
思路一:
上大佬的代码:
1 import bisect 2 3 4 class TreeSet(object): 5 """ 6 Binary-tree set like java Treeset. 7 Duplicate elements will not be added. 8 When added new element, TreeSet will be sorted automatically. 9 """ 10 def __init__(self, elements): 11 self._treeset = [] 12 self.addAll(elements) 13 14 def addAll(self, elements): 15 for element in elements: 16 if element in self: continue 17 self.add(element) 18 19 def add(self, element): 20 if element not in self: 21 bisect.insort(self._treeset, element) 22 23 def ceiling_index(self, e, exclusive=False): 24 index = bisect.bisect_right(self._treeset, e) 25 if exclusive: 26 return index 27 if index > 0 and self[index - 1] == e: 28 return index - 1 29 return index 30 31 def floor_index(self, e, exclusive=False): 32 index = bisect.bisect_left(self._treeset, e) 33 if exclusive: 34 return index - 1 35 if index < len(self) and self[index] == e: 36 return index 37 return index - 1 38 39 def ceiling(self, e, exclusive=False): 40 index = self.ceiling_index(e, exclusive) 41 if 0 <= index < len(self): 42 return self[index] 43 return None 44 45 def floor(self, e, exclusive=False): 46 index = self.floor_index(e, exclusive) 47 if 0 <= index < len(self): 48 return self[index] 49 return None 50 51 def __getitem__(self, num): 52 return self._treeset[num] 53 54 def __len__(self): 55 return len(self._treeset) 56 57 def clear(self): 58 """ 59 Delete all elements in TreeSet. 60 """ 61 self._treeset = [] 62 63 def clone(self): 64 """ 65 Return shallow copy of self. 66 """ 67 return TreeSet(self._treeset) 68 69 def remove(self, element): 70 """ 71 Remove element if element in TreeSet. 72 """ 73 try: 74 self._treeset.remove(element) 75 except ValueError: 76 return False 77 return True 78 79 def __iter__(self): 80 """ 81 Do ascending iteration for TreeSet 82 """ 83 for element in self._treeset: 84 yield element 85 86 def pop(self, index): 87 return self._treeset.pop(index) 88 89 def __str__(self): 90 return str(self._treeset) 91 92 def __eq__(self, target): 93 if isinstance(target, TreeSet): 94 return self._treeset == target.treeset 95 elif isinstance(target, list): 96 return self._treeset == target 97 98 def __contains__(self, e): 99 """ 100 Fast attribution judgment by bisect 101 """ 102 try: 103 return e == self._treeset[bisect.bisect_left(self._treeset, e)] 104 except: 105 return False 106 107 108 class TreeMap(dict): 109 """ 110 "TreeMap" is a dictionary with sorted keys similar to java TreeMap. 111 Keys, iteration, items, values will all return values ordered by key. 112 Otherwise it should behave just like the builtin dict. 113 """ 114 115 def __init__(self, seq=None, **kwargs): 116 if seq is None: 117 super().__init__(**kwargs) 118 else: 119 super().__init__(seq, **kwargs) 120 self.sorted_keys = TreeSet(super().keys()) 121 122 def __setitem__(self, key, value): 123 super().__setitem__(key, value) 124 self.sorted_keys.add(key) 125 126 def __delitem__(self, key): 127 super().__delitem__(key) 128 self.sorted_keys.remove(key) 129 130 def keys(self): 131 return self.sorted_keys 132 133 def items(self): 134 return [(k, self[k]) for k in self.sorted_keys] 135 136 def __iter__(self): 137 for k in self.sorted_keys: 138 yield k 139 140 def values(self): 141 for k in self.sorted_keys: 142 yield self[k] 143 144 def clear(self): 145 super().clear() 146 self.sorted_keys.clear() 147 148 def ceiling_index(self, e, exclusive=False): 149 return self.sorted_keys.ceiling_index(e, exclusive) 150 151 def floor_index(self, e, exclusive=False): 152 return self.sorted_keys.floor_index(e, exclusive) 153 154 def ceiling_key(self, e, exclusive=False): 155 return self.sorted_keys.ceiling(e, exclusive) 156 157 def floor_key(self, e, exclusive=False): 158 return self.sorted_keys.floor(e, exclusive) 159 160 def ceiling_value(self, e, exclusive=False): 161 key = self.ceiling_key(e, exclusive) 162 return self[key] if key is not None else None 163 164 def floor_value(self, e, exclusive=False): 165 key = self.floor_key(e, exclusive) 166 return self[key] if key is not None else None 167 168 169 class BlackBox: 170 171 def __init__(self, n: int, m: int): 172 self.groupPos, self.groupNeg, self.groupStats = [], [], [] 173 ptCount = (n + m) * 2 174 self.groupPos, self.groupNeg = [(-1, -1) for _ in range(ptCount)], [(-1, -1) for _ in range(ptCount)] 175 for i in range(ptCount): 176 # 如果不是左上角或者右下角的小孔,那么从 y=x 方向射出找循环 177 if i != 0 and i != m + n and self.groupPos[i][0] == -1: 178 self.createGroup(n, m, i, 1) 179 # 如果不是左下角或者右上角的小孔,那么从 y=-x 方向射出找循环 180 if i != m and i != m * 2 + n and self.groupNeg[i][0] == -1: 181 self.createGroup(n, m, i, -1) 182 183 def createGroup(self, n: int, m: int, index: int, direction: int): 184 groupId = len(self.groupStats) 185 groupLoc = 0 186 self.groupStats.append(TreeMap()) 187 # 不断模拟光线的路径,直到走到一个已经遇见过的状态,这样就找到了一个循环 188 while not (direction == 1 and self.groupPos[index][0] != -1) and not (direction == -1 and self.groupNeg[index][0] != -1): 189 if direction == 1: 190 self.groupPos[index] = (groupId, groupLoc) 191 index = (n + m) * 2 - index 192 else: 193 self.groupNeg[index] = (groupId, groupLoc) 194 index = m * 2 - index if index <= m * 2 else (m * 2 + n) * 2 - index 195 # 如果小孔不在角上,就改变方向 196 if index != 0 and index != m and index != m + n and index != m * 2 + n: 197 direction = -direction 198 groupLoc += 1 199 200 def open(self, index: int, direction: int) -> int: 201 # 插入二元组 202 groupId, groupLoc = self.groupPos[index] 203 if groupId != -1: 204 self.groupStats[groupId][groupLoc] = index 205 groupId, groupLoc = self.groupNeg[index] 206 if groupId != -1: 207 self.groupStats[groupId][groupLoc] = index 208 209 # 查询 210 groupId, groupLoc = self.groupPos[index] if direction == 1 else self.groupNeg[index] 211 store = self.groupStats[groupId] 212 ceiling = store.ceiling_value(groupLoc, exclusive=True) 213 if ceiling: 214 return ceiling 215 return store[store.keys()[0]] 216 217 def close(self, index: int) -> None: 218 # 删除二元组 219 groupId, groupLoc = self.groupPos[index] 220 if groupId != -1: 221 del self.groupStats[groupId][groupLoc] 222 groupId, groupLoc = self.groupNeg[index] 223 if groupId != -1: 224 del self.groupStats[groupId][groupLoc] 225 226 227 ##作者:acst 228 ##链接:https://leetcode-cn.com/problems/IQvJ9i/solution/yu-chu-li-chu-suo-you-de-xun-huan-_python3ban-ben-/ 229 ##来源:力扣(LeetCode) 230 ##著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
预处理出所有的循环_Python3版本
将@zerotrac2大佬的解法译成了Python3的代码。
由于Python没有如C++和Java方便的TreeSet和TreeMap库。因此只能自己造轮子。。这里在https://github.com/fukatani/TreeSet代码的基础上进行了一些修改,做了Python3的TreeSet和TreeMap类。改进后的这两个类也可以直接使用https://github.com/acst1223/always_code/tree/master/python中treeset.py
和treemap.py
的代码
经@apoi2333提醒,Python有Sorted Containers这个库可以用,大家也可以去了解一下~
我自己的代码(轮子)如下,前面的大段代码均为TreeSet和TreeMap类内容。
知识扩充——bisect:
一个有趣的python排序模块:bisect
今天同事说到了一个python的排序模块bisect,觉得挺有趣的,跟大家分享分享。
先看看模块的结构:
前面五个属性大家感兴趣可以打出来看看数值,这里就不介绍了。
先说明的是,使用这个模块的函数前先确保操作的列表是已排序的。
先看看 insort 函数:
其插入的结果是不会影响原有的排序。
再看看 bisect 函数:
其目的在于查找该数值将会插入的位置并返回,而不会插入。
接着看 bisect_left 和 bisect_right 函数,该函数用入处理将会插入重复数值的情况,返回将会插入的位置:
其对应的插入函数是 insort_left 和 insort_right :
可见,单纯看其结果的话,两个函数的操作结果是一样的,其实插入的位置不同而已。
思路二:
红黑树解法,先上大佬的代码:
1 const int N=1E5; 2 int n, m; 3 int index2map[N]; 4 int index2cmp_val[N]; 5 int map_allo = 0; 6 7 struct comps { 8 bool operator()(int i, int j) const{ 9 return index2cmp_val[i] < index2cmp_val[j]; 10 } 11 }; 12 13 map<int, int, comps> maps[N];//1E5 14 15 class BlackBox { 16 public: 17 18 inline int trans(int i, int k) { 19 return i * 2 + (k == -1 ? 0 : 1); 20 } 21 22 inline int corner(int x) { 23 return x == 0 || x == m - 1 || x == m + n - 1 || x == 2 * m + n - 1; 24 } 25 26 inline int getside(int x) { 27 if (x < m)return 0; 28 if (x < m + n)return 1; 29 if (x < 2 * m + n)return 2; 30 return 3; 31 } 32 33 34 int get_next(int x) { 35 int pos = x / 2; 36 int k = x % 2 ? 1 : -1; 37 int s = getside(pos); 38 int d = 0; 39 if (s == 0) { 40 if (k == 1) { 41 d = 2 * (m - pos); 42 } else { 43 d = -2 * (pos); 44 } 45 46 } else if (s == 1) { 47 if (k == 1) { 48 d = -2 * (pos - m); 49 } else { 50 d = 2 * (m + n - pos); 51 } 52 } else if (s == 2) { 53 if (k == 1) { 54 d = 2 * (2 * m + n - pos); 55 } else { 56 d = -2 * (pos - m - n); 57 } 58 } else { 59 if (k == 1) { 60 d = -2 * (pos - 2 * m - n); 61 } else { 62 d = 2 * (2 * m + 2 * n - pos); 63 } 64 } 65 return trans((pos + d + 2 * m + 2 * n) % (2 * m + 2 * n), -1 * k); 66 } 67 68 BlackBox(int nn, int mm) { 69 memset(index2map,0,sizeof index2map); 70 memset(index2cmp_val,0,sizeof index2cmp_val); 71 n = nn; 72 m = mm; 73 for (int i = 0; i < 4 * m + 4 * n; i++) { 74 if (index2map[i])continue; 75 map_allo++; 76 maps[map_allo].clear(); 77 int temp = i; 78 int val = 1; 79 while (1) { 80 index2map[temp] = map_allo; 81 index2cmp_val[temp] = val++; 82 temp = get_next(temp); 83 if (temp == i)break; 84 } 85 } 86 87 88 } 89 90 int open(int index, int direction) { 91 int temp1 = trans(index, -direction); 92 int temp2 = trans(index, direction); 93 maps[index2map[temp1]][temp1] = 1; 94 maps[index2map[temp2]][temp2] = 1; 95 96 auto it = maps[index2map[temp1]].upper_bound(temp1); 97 if (it == maps[index2map[temp1]].end()) { 98 return maps[index2map[temp1]].begin()->first/2; 99 } 100 return it->first/2; 101 } 102 103 void close(int index) { 104 int temp1 = trans(index, 1); 105 int temp2 = trans(index, -1); 106 maps[index2map[temp1]].erase(temp1); 107 maps[index2map[temp2]].erase(temp2); 108 } 109 }; 110 111 112 //作者:levyjeng 113 //链接:https://leetcode-cn.com/problems/IQvJ9i/solution/hong-hei-shu-jie-fa-by-levyjeng/ 114 //来源:力扣(LeetCode) 115 //著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
主要就是在预处理阶段找出所有的反射循环,每个循环都存在一个红黑树里面,open的时候,就往该红黑树里面添加该位置并找出距离该位置最近的那个开放位置,而close的时候,就在相应的红黑树里面移除掉该位置
知识扩充——红黑树:
详见印象笔记:https://app.yinxiang.com/fx/382ee9f5-7a1e-4b97-a2b8-73cd11ad1eaa
思路差距:
一、理解题目完后就犯了一个很大的错误,没有对模型进行更深入的解析,只能停留在表面的物理模型,导致计算复杂度爆炸,下次接触类似算法题目时应该透过现象看本质,在物理层面下提炼其数字逻辑上的关系
二、这也是创建本博客的初衷,就是希望把刷题的效果最大化,刷题可以提高能力,“刷”的本身只占很小一部分,它提高的部分在于你的调用思维的速度,编码速度,编程语言的熟练度,但是也不能直接跳过刷的部分,就算花掉许多时间也一定要把自己的思路完成实现,不然永远不能让思路成熟。而真正提高的部分在于刷完的“分析”,这其中包括找到自己容易犯的错误,找到自己的思维、技术漏洞、与大佬的差距,分析的过程也是学习、模仿的过程。切忌浮于表面!(虽然这一条就很浮于表面(x))
三、思路一、思路二的都是对反射规律进行了提炼,从一般性的光线反射问题提炼出了一套数字模型,即双向的循环结构,并结合双向的循环结构巧妙地套用了树形数据结构,区别之处在于思路一构造了树形排序、类似字典检索的数据结构,思路二直接套用红黑树。
技术差距:
一、反观自己写的代码,可以发现它过于冗长,有大段的重复,这不是一个好现象,这是算法设计不好导致的,而且代码的结构、函数的参数也没经过事先的设计,这拖慢了编码速度,下次在构思完算法思路后对代码结构进行设计。
二、python语言应用不够成熟,语句都是生硬的固定语法,离灵活运用还有不少的差距,需要在大量练习中进步。在类定义构建、类实体化等操作中还会犯低级语法、思路错误,这是数据结构、python语言机理等方面掌握不牢固导致的,还需要大量练习。
三、过多数量的多重遍历,或者说过于依赖多重遍历,榨干了机能(x)(平时看了不少cv代码,导致不看重遍历的优化,这句话冒犯cv工程师了吗hhhhh),严重降低了代码的性能,应该考虑优化其中的某些遍历,将其去掉或者进行改良。
四、思路一中的代码结构设计,作者是将另外一位大佬的C++改编成了python,改编带来的问题就是,python没有现成的数据结构,于是着手造了一个树形排序结构、一个继承字典查询结构,后接一个黑盒类,代码精炼清晰,类专有方法调用、重写、应用如数家珍,足以看出python基础扎实,代码功力深厚(力扣题解不是随随便便就敢往上放的,BATH牛人数不胜数),这里也能看出我的差距,对数据结构、python基础方法、专有方法生疏到令人发指,再不恶补铁定要被远远甩后面~
五、思路二由C++实现,我看不太懂(菜鸡本鸡),大致是先确定每个孔的方位(上下左右分别用0、1、2、3表示),然后推动光线“传播”,当然这个传播是经过预处理模型简化的,传播进入循环阶段后就沿用红黑树查询节点重复状态。