📌  相关文章
📜  给定数组的K个反转后的最大前缀和(1)

📅  最后修改于: 2023-12-03 14:56:54.542000             🧑  作者: Mango

给定数组的K个反转后的最大前缀和

问题描述

给定一个长度为n的整数数组a和一个非负整数k,每次操作可以将a数组中的一个区间[l, r]全部反转(即a[i]变成a[l+r-i])。求进行不超过k次操作后的最大前缀和。

算法分析

考虑贪心,每次我们需要找到目前a数组中的最大值max。若max大于0,则操作区间[l, r]应该是包含max的,否则我们操作空区间。对于每个max都进行这样的操作,直到操作次数超过k或者不存在正数。

而在进行操作时,我们需要保留目前已经反转了多少数字,以便计算最大前缀和。注意到我们每次操作一段区间[l, r],会对[l-lmax, r+lmax]上的数字的顺序进行改变。因此,当我们完成一次操作后,需要将操作区间的端点更新为[l-lmax, r+lmax](其中lmax为目前反转的数字数量),并将最大前缀和更新为当前前缀和与[l-lmax, r+lmax]的最大前缀和之和。

代码实现
def max_prefix_sum(a, k):
    """
    a: List[int],长度为n的整数数组
    k: int,非负整数,表示可以进行的操作次数
    return: int,进行不超过k次操作后的最大前缀和
    """
    n = len(a) # 数组长度
    l = 0 # 最大值所在区间的左端点(包含)
    r = 0 # 最大值所在区间的右端点(包含)
    lmax = 0 # 已经反转了的数字数量
    ans = 0 # 最大前缀和
    while k > 0 and l < n: # 当还可以进行操作且还未遍历数组
        # 找到目前a数组中的最大值max
        while r < n and (r <= l or a[r] <= 0): 
            r += 1
        if r >= n: # 已经没有正数
            break
        
        max_index = r
        for i in range(r+1, n):
            if a[i] > a[max_index]:
                max_index = i
            if a[max_index] > 0:
                break
        
        max_value = a[max_index]
        
        # 计算操作区间[l, r]和[l-lmax, r+lmax]        
        left = max(l, max_index-lmax)
        right = min(r, max_index+lmax)
        left_rev = n - right - 1
        right_rev = n - left - 1
        
        # 反转[l, r]
        for i in range((l+max_index)//2, max_index):
            a[i], a[l+max_index-i] = a[l+max_index-i], a[i]
        lmax += right-max_index+1
        
        # 反转[l-lmax, r+lmax]
        for i in range((left+right)//2, right+1):
            a[i], a[left+right-i] = a[left+right-i], a[i]
        lmax += right_rev-right
        
        # 更新区间[l, r]和前缀和        
        l = left_rev
        r = right_rev
        ans = max(ans, sum(a[l:right+1])+sum(a[left:right+1]))
        k -= 1
            
    return ans