• leetcode常规算法题复盘(第一期)——黑盒光线反射


    题目原文:

    LCP 27. 黑盒光线反射

    秋日市集上有个奇怪的黑盒,黑盒的主视图为 n*m 的矩形。从黑盒的主视图来看,黑盒的上面和下面各均匀分布有 m 个小孔,黑盒的左面和右面各均匀分布有 n 个小孔。黑盒左上角小孔序号为 0,按顺时针编号,总共有 2*(m+n) 个小孔。每个小孔均可以打开或者关闭,初始时,所有小孔均处于关闭状态。每个小孔上的盖子均为镜面材质。例如一个 2*3 的黑盒主视图与其小孔分布如图所示:

    image.png

    店长告诉小扣,这里是「几何学的快问快答」,店长可能有两种操作:

    • open(int index, int direction) - 若小孔处于关闭状态,则打开小孔,照入光线;否则直接照入光线;
    • close(int index) - 关闭处于打开状态小孔,店长保证不会关闭已处于关闭状态的小孔;

    其中:

    • index: 表示小孔序号
    • direction1 表示光线沿 y=xy=xy=x 方向,-1 表示光线沿 y=−xy=-xy=x 方向。

    image.png

    当光线照至边界时:若边界上的小孔为开启状态,则光线会射出;否则,光线会在小孔之间进行反射。特别地:

    1. 若光线射向未打开的拐角(黑盒顶点),则光线会原路反射回去;
    2. 光线自拐角处的小孔照入时,只有一种入射方向(如自序号为 0 的小孔照入方向只能为 -1

    image.png

    请帮助小扣判断并返回店长每次照入的光线从几号小孔射出。

    示例 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]

    解释:

    image.png

    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版本

    acst发布于 2020-09-2296Python3

    @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.pytreemap.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表示),然后推动光线“传播”,当然这个传播是经过预处理模型简化的,传播进入循环阶段后就沿用红黑树查询节点重复状态。

  • 相关阅读:
    循环神经网络(LSTM和GRU)(1)
    threading包的例子和queue包的例子
    xgboost调参
    理解 Python 中的 *args 和 **kwargs
    TFRecords文件的生成和读取(1)
    tensorflow函数介绍(4)
    python其他篇(1)
    python实现Restful服务(基于flask)(2)
    开源多线程性能测试工具-sysbench
    MySQL 8.0.0 版本发布,亮点都在这了!
  • 原文地址:https://www.cnblogs.com/monkiki/p/13854393.html
Copyright © 2020-2023  润新知