LCP 49-环形闯关游戏
「力扣挑战赛」中有一个由 N
个关卡组成的环形闯关游戏,关卡编号为 0
~`N-1,编号
0的关卡和编号
N-1 的关卡相邻。每个关卡均有积分要求,
challenge[i]表示挑战编号
i的关卡最少需要拥有的积分。 ![图片.png](https://pic.leetcode- cn.com/1630392170-ucncVS-%E5%9B%BE%E7%89%87.png){:width="240px"} 小扣想要挑战关卡,闯关具体规则如下: \- 初始小扣可以指定其中一个关卡为「开启」状态,其余关卡将处于「未开启」状态。 \- 小扣可以挑战处于「开启」状态且**满足最少积分要求**的关卡,若小扣挑战该关卡前积分为
score,挑战结束后,积分将增长为
score|challenge[i](即位运算中的
“OR” 运算) \- 在挑战某个关卡后,该关卡两侧相邻的关卡将会开启(若之前未开启) 请帮助小扣进行计算,初始最少需要多少积分,可以挑战 **环形闯关游戏** 的所有关卡。 **示例1:** > 输入:
challenge =
[5,4,6,2,7] > > 输出:
4 > > 解释: 初始选择编号 3 的关卡开启,积分为 4 >挑战编号 3 的关卡,积分变为 4 | 2 = 6,开启 2、4 处的关卡 >挑战编号 2 的关卡,积分变为 6 | 6 = 6,开启 1 处的关卡 >挑战编号 1 的关卡,积分变为 6 | 4 = 6,开启 0 处的关卡 >挑战编号 0 的关卡,积分变为 6 | 5 = 7 >挑战编号 4 的关卡,顺利完成全部的关卡 **示例2:** > 输入:
challenge = [12,7,11,3,9] > > 输出:
8` > > 解释: 初始选择编号 3 的关卡开启,积分为 8 >挑战编号 3
的关卡,积分变为 8 | 3 = 11,开启 2、4 处的关卡 >挑战编号 2 的关卡,积分变为 11 | 11 = 11,开启 1 处的关卡
挑战编号 4 的关卡,积分变为 11 | 9 = 11,开启 0 处的关卡 >挑战编号 1 的关卡,积分变为 11 | 7 = 15 >挑战编号
0 的关卡,顺利完成全部的关卡 示例3: > 输入:challenge = [1,1,1]
> > 输出:1
提示: -1 <= challenge.length <= 5*10^4
-1 <= challenge[i] <= 10^14
看完这道题目的描述,我注意到这几个关键点:
1、如果拿关卡挑战积分的最大值作为初始积分a,一定能完成所有挑战。
2、在关卡的挑战过程中,积分变化是二进制位或操作,在挑战过程中,初始积分的某些为0的二进制位会被补成1。
3、题目的目的是要找最小初始积分,那么我们可以尝试改变m的某些二进制位,最小化成b。
为了实现思路3,我发现:
1、a、b,在二进制表示时,从高到低位,第一位不同必然是a为1,b为0
2、而a表示为二进制1xxxxx中最高位的1是必须的,因为挑战过程中,积分在没有这个1的时候,无法挑战(小于)含有这个1的关卡,永远得不到这个1;
3、继续找下一个1,判断这个1是否可以不要?正好 x..x1y..y > x..x01..1(且这是前面部分x..x确定时,剩下最高二进制位为0,最大可能能完成所有挑战的初始积分),如果x..x01..1不能完成完成所有关卡,那么这个位需要为1,否则可以为0
4、不断进行3的操作,就能最终找到答案。
有了算法,当然需求考虑一下时间复杂度:
算法步骤3,直接暴力判断(用x..x01..1作为初始积分,分别试用每一个关卡作为起点,向两边扩展),复杂度是O(n^2)
算法步骤4,最多就是60次(2^(6*10) > 10^18)
所以步骤3需要优化,想到两个突破口:
1、从一个关卡作为起点,扩展经过的点都可以不必再尝试作为起点;
2、从不同起点向左向右扩展时,很多时候的计算是重复的,可以提前预处理,在尝试答案前,计算出以某个关卡为起点(也以它的挑战分为初始分),单纯向左或向右能扩展多远。这一步预处理时间是O(n),能节约步骤3、4里大量计算。
(其它题解有最坏情况算法复杂度更低的思路,我这里不赘述了。不过有意思的是,跑实际数据,仅用优化2更快)
1 | class Solution: |