难度: Hard
Submission
运行时间: 441 ms
内存: 32.2 MB
from sortedcontainers import SortedList from typing import List from collections import defaultdict, deque from itertools import accumulate import heapq import sys ''' 1 设n为结果 max_n/2 <= n <= max 2 m为去头 结果为 max_n/2 | m 3 最大 - 其它的| 必在 m 里 4 < 4 dp[0][n]=s d ''' class Solution: def log(self, *s): pass def ringGame(self, challenge: List[int]) -> int: n=len(challenge) max_value=challenge[0] max_index=0 for i in range(1,len(challenge)): if challenge[i]>max_value: max_value=challenge[i] max_index=i challenge = challenge[max_index:]+challenge+challenge[:max_index+1] n3=len(challenge) def check(m) : i = 1 while i < n3: # 尝试以关卡i为起点 if challenge[i] > m : i += 1 continue s, l, r = m|score_left[i]|score_right[i], idx_left[i], idx_right[i] while True : if r-l > n : return True # 已挑战完所以关卡 # 向两边挑战关卡,并更新积分 if challenge[l] <= s : s |= score_left[l] l = idx_left[l] if challenge[r] <= s : s |= score_right[r] r = idx_right[r] else : break i = r # 扩展经过的点都可以不必再尝试作为起点; return False # 预计算出以某个关卡为起点(也以它的挑战分为初始分),单纯向左或向右能扩展多远和扩展后的积分 score_left, idx_left = challenge[:], [0]*n3 for i in range(n3) : j = i-1 while j >= 0 and challenge[j] <= score_left[i] : score_left[i] |= score_left[j] j = idx_left[j] idx_left[i] = j score_right, idx_right = challenge[:], [0]*n3 for i in range(n3-1, -1, -1) : j = i+1 while j < n3 and challenge[j] <= score_left[i] : score_right[i] |= score_right[j] j = idx_right[j] idx_right[i] = j # self.log(challenge) # self.log(score_right) # self.log(idx_right) # 从高到低确定答案的二进制位 bit = 1 << (max_value.bit_length()-1) res = bit while bit: bit >>= 1 if not check(res | (bit-1)) : res |= bit return res def baoli(self,challenge): def util(ret,i): visited=[False]*len(challenge) stacks=[] heapq.heappush(stacks,[challenge[i],i]) while len(stacks): next_v,next_i=heapq.heappop(stacks) if next_v>ret: return False visited[next_i]=True ret = ret|next_v next_i_right=(next_i+1)%len(challenge) next_i_left=(next_i-1+len(challenge))%len(challenge) if not visited[next_i_right]: heapq.heappush(stacks,[challenge[next_i_right],next_i_right]) if not visited[next_i_left]: heapq.heappush(stacks,[challenge[next_i_left],next_i_left]) return True def check(ret): for i in range(len(challenge)): if challenge[i]<=ret and util(ret,i): return True return False while ret<max_score: mid=(ret+max_score)//2 self.log(ret,mid,max_score) if check(mid): max_score=mid else: ret=mid+1 return max_score def run(self, *args): return self.ringGame(*args) class SolutionDebug(Solution): logs = "" def log(self, *s): self.logs += " ".join([str(v) for v in s])+" " def run(self, *args, **kw) -> int: self.logs = "" return super().run(*args, **kw) if __name__ == '__main__': s = SolutionDebug() null=None true=True false=False for case in [ [[1,180,447],266], [[5,4,6,2,7],4], [[12,7,11,3,9],8], [[7,7],7], [[1,17,1,19,1],18], [[1,1,1],1] ]: s.logs = "" e = s.run(*case[:-1]) if e != case[-1]: print(e, case) print(s.logs) break
Explain
此题解的核心思想是利用二分搜索和双指针技术来寻找最小的初始积分,以挑战所有关卡。首先,找到挑战值最大的关卡,并以此关卡为基点重构环形数组为线性数组,以简化问题。然后,使用二分搜索从可能的最低积分开始,使用双指针检测该积分是否足以开始并完成游戏。在二分搜索的每一步,会尝试将挑战分和数组两侧的积分合并,检查能否覆盖整个数组。这种方法有效地将环形结构转化为线性结构,从而简化了逻辑判断。
时间复杂度: O(n log(max_value))
空间复杂度: O(n)
from sortedcontainers import SortedList from typing import List from collections import defaultdict, deque from itertools import accumulate import heapq import sys class Solution: def log(self, *s): pass # 日志函数,用于调试 def ringGame(self, challenge: List[int]) -> int: n = len(challenge) max_value = max(challenge) max_index = challenge.index(max_value) # 重构数组使其线性化,简化问题 challenge = challenge[max_index:] + challenge[:max_index] n3 = len(challenge) def check(m): # 检查起始积分m是否足够挑战所有关卡 for i in range(1, n3): if challenge[i] > m: continue s, l, r = m | challenge[i], i - 1, i + 1 while True: if r - l > n: return True if l >= 0 and challenge[l] <= s: s |= challenge[l] l -= 1 if r < n3 and challenge[r] <= s: s |= challenge[r] r += 1 else: break return False # 二进制搜索最低有效积分 res = 1 << max_value.bit_length() - 1 while res: if not check(res): res <<= 1 else: res >>= 1 return res def run(self, *args): return self.ringGame(*args) if __name__ == '__main__': s = Solution() result = s.run([5,4,6,2,7]) print(result) # 应该输出4
Explore
将环形数组转换为线性数组是为了简化问题的处理。在环形结构中,数组的末尾与开头相连,这使得处理边界条件和遍历时的逻辑变得复杂。通过将环形数组以最大值所在的位置为断点重构为线性数组,可以将问题从一个循环处理问题转变为一个更容易管理的线性处理问题。这种转换使得数组的遍历和检查变得直接明了,减少了复杂的边界判断,从而提高了代码的可读性和易于管理。
在题解中,二分搜索的初始下界设为0,上界设为最大关卡挑战值的二进制长度左移1位的结果。具体来说,上界的计算方法是将最大挑战值的二进制表示长度减一后,结果为1的位数左移一位。这样做是基于对问题的理解:一个合理的起始上界应该足够大,能够覆盖最大的单一挑战,并且还应有余地以确保可以通过其他较小的挑战。这种设置上界的方法是为了确保在二分搜索开始时,有一个足够宽的搜索范围来逐步缩小至最优解。
双指针策略在本题中是通过设置一个左指针和一个右指针来实现的,这两个指针分别从当前考察的关卡的左右两侧开始移动。在每次二分搜索尝试的过程中,会从挑战数组中的某一点开始,用当前的积分尝试向左右两边扩展。如果当前积分足够覆盖左侧或右侧的下一个关卡,相应的指针就会移动一位,并将该关卡的挑战值合并入当前积分。这个过程会一直持续,直到指针超出数组边界或无法继续向某方向扩展(因为挑战值大于当前积分)。如果在某次尝试中,左右指针能够覆盖整个数组,那么说明当前积分足够通过所有关卡。