題目原文:
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表示),然后推動光線“傳播”,當然這個傳播是經過預處理模型簡化的,傳播進入循環階段后就沿用紅黑樹查詢節點重復狀態。