📅  最后修改于: 2023-12-03 15:23:05.701000             🧑  作者: Mango
给定一个圆形整数数组,找到一个子数组,使得该子数组的元素之和最大,要求不能选择相邻的两个元素。
两套解法:动态规划和状态压缩。
首先,我们可以将圆形数组转化为一条直线数组,令 $dp[i]$ 表示以第 $i$ 个数字为结尾的最大子序和,则:
$$ dp[i] = \max{dp[i-2]+nums[i], dp[i-3]+nums[i]} $$
其中 $nums$ 为原始数组,如果我们选择了第 $i$ 个数字,则不能选择第 $i-1$ 个数字。因此,我们将状态分为两种情况:选择了 $i$ 和未选择 $i$。未选择 $i$ 的情况包括 $i-1$ 是否选择都可以。
因为该数组是圆形的,所以我们需要分别处理两种情况:不选数组的第一个和最后一个元素。最终的答案即为:
$$ \max{dp[n-1], dp[n-2]} $$
其中 $n$ 为数组长度。
时间复杂度:$O(n)$
第二种方法是使用状态压缩。我们可以将每个元素分为两种状态:包含和不包含。用 $S$ 表示一个状态,则:
$$ S = (S << 1) & k + nums[i] $$
其中 $<<$ 表示左移操作,$&$ 表示按位与操作,$k$ 是一个二进制数,表示最多相邻的 $1$ 的个数。由于 $nums[i]$ 的取值范围为 $[-10000, 10000]$,所以 $k$ 的取值范围为 $[0, 14]$。
最终的答案即为:
$$ \max{dp[k], dp[k-1]} $$
其中 $dp[k]$ 表示所有状态包含 $k$ 个 $1$ 的状态的最大和,$dp[k-1]$ 表示所有状态包含 $k-1$ 个 $1$ 的状态的最大和。
时间复杂度:$O(n)$
def max_sum(nums):
n = len(nums)
if n == 1:
return nums[0]
dp1 = [0] * (n+1)
dp2 = [0] * (n+1)
dp1[0] = dp2[1] = nums[0]
dp1[1] = nums[1]
dp2[2] = nums[2]
for i in range(2, n):
dp1[i] = max(dp1[i-2]+nums[i], dp1[i-1])
dp2[i+1] = max(dp2[i-1]+nums[i], dp2[i])
return max(dp1[n-2], dp2[n-1])
def max_sum(nums):
n = len(nums)
if n == 1:
return nums[0]
dp = [0] * 2
for i in range(n):
new_dp = [0] * 2
for j in range(2):
s = ((dp[j] << 1) & ((1 << 15) - 1)) + nums[i]
new_dp[s >> 14] = max(new_dp[s >> 14], dp[j] + nums[i])
dp = new_dp
return max(dp)
本题考察了对动态规划和状态压缩的理解和应用。在解题过程中,注意考虑数组为圆形的情况,以及边界条件的处理。同时,状态转移方程也需要根据实际情况设计。最后,可以考虑使用两种方法解题,从而加深对这两种算法的理解和应用。