一、 题目
1. 题目描述
2. 原题链接
链接: LCP 49. 环形闯关游戏
二、 解题报告
1. 思路分析
预处理按位贪心。
- 假设我们存在一个方法check(val),可以查询当前数字是否存在方案成功闯关。
- 那么,我们可以从高位到低位遍历每个位1是否是必须要:
- 即,如果要判断二进制s=10000,s的最右的这个1是否需要,那只需要判断s-1=01111是否可以通关,如果可以,那这位就不需要;
- 如果不可以通关,那这位就是必要的。
- 因此可以初始化ans=0,然后从高到低逐位判断ans|(s-1)是否可以通关,如果可以就不要这位s;否则必须要,ans|=s
- 那么check(val)方法怎么实现呢。
- 只能尝试每一位作为起点,然后向左右扩展,显然一次时间复杂度是O(n^2)。
- 那么需要剪枝。两个方案。
-
- 每次check内设置visited,如果前边没能成功的起点已经途径过这个点,那么不需要它作为起点了。因为它作为途经,当时的状态不可能小于自己,都转不完,那它作为起点更转不完了。
-
- 预处理一下challenge数组,计算每个点作为起点且起始分正好是它自己的话,左右最远能延伸到哪,那就可以直接跳到左右两端了。
- 最坏时间复杂度没变,但是能过了。
2. 复杂度分析
最坏时间复杂度O(n × 61)
3. 代码实现
class Solution:
def ringGame(self, challenge: List[int]) -> int:
n = len(challenge)
vis = [-1]*n
nxt=lambda i:(i+1) if i < n-1 else 0
pre = lambda i :i-1 if i > 0 else n-1
# d = challenge+challenge
# n2 = n*2
# left = list(range(n2))
# left_s = d[:]
# for i in range(1,n2):
# if d[i] >= d[i-1]:
# left[i] = left[i-1]
# left_s[i-1] |= left_s[i-1]
# left = [left[i]%n for i in range(n,n2)]
# left_s = left_s[:n]
# right = list(range(n2))
# right_s = d[:]
# for i in range(n2-2,-1,-1):
# if d[i]>= d[i+1]:
# right[i] = right[i+1]
# right_s[i] != right_s[i+1]
# right = [right[i]%n for i in range(n)]
# right_s = right_s[:n]
d = challenge+challenge
n2 = n*2
left = list(range(n2))
left_s = d[:]
for i in range(1,n2):
j = i - 1
while j >= 0 and left_s[i] >= d[j]:
left_s[i] |= left_s[j]
left[i] = left[j]
j =left[j] - 1
left = [left[i]%n for i in range(n,n2)]
left_s = left_s[:n]
right = list(range(n2))
right_s = d[:]
for i in range(n2-2,-1,-1):
j = i + 1
while j < n2 and right_s[i] >= d[j]:
right_s[i] |= right_s[j]
right[i] = right[j]
j = right[j] + 1
right = [right[i]%n for i in range(n)]
right_s = right_s[:n]
# print(left_s,right_s)
# print(left_s,right_s)
def check(val):
for i in range(n):
if val < challenge[i]:
continue
# if vis[i] == val:
# continue
# s = val|challenge[i]
# # vis[i] = val
# l = r = i
s = val | left_s[i] | right_s[i]
l,r = left[i],right[i]
while True:
# print(s,l,r,i)
if l == nxt(r):return True # 成环
if s >= challenge[pre(l)]: # 左右尝试吃
l = pre(l)
# vis[l] = val
s |= challenge[l]
elif s >= challenge[nxt(r)]:
r = nxt(r)
# vis[r] = val
s |= challenge[r]
else: # 吃不到就完了
break
return False
s = 1<<62
ans = 0
while s:
if check((ans|s)-1) == False:
ans |= s
s >>= 1
# print(check(3,4))
return ans
三、 本题小结
- 按位贪心,第一次写