【备战秋招】每日一题:小红书提前批-7月23日-第三题-魔法项链

文章描述了一种算法问题,要求在给定数组中,通过最多一次将某个元素修改为特定值X,找到连续子数组的最大和。解决方案包括使用动态规划分别从左右两端计算最大子数组和,并针对每个位置的修改进行计算,取最大值作为最终答案。
摘要由CSDN通过智能技术生成

为了更好的阅读体检,可以查看我的算法学习网
本题在线评测链接:P1405

题目描述

小红拿到了一个数组,她希望进行最多一次操作: 将一个元素修改为 X X X。小红想知道,最终的连续子数组最大和最大是多少?

输入描述

第一行输入一个正整数 t t t,代表询问次数。
对于每次询问,输入两行:
第一行输入两个正整数 n n n x x x。代表数组的大小,以及小红可以修改成的元素。

第二行输入 n n n个正整数 a i a_i ai,代表小红拿到的数组。

1 ≤ t ≤ 100000 1 \leq t \leq 100000 1t100000

1 ≤ n ≤ 200000 1 \leq n \leq 200000 1n200000

− 1 0 9 ≤ x , a 1 ≤ 1 0 9 -10^9 \leq x,a_1 \leq 10^9 109x,a1109

每组所有询问的 n n n的和不超过 200000 200000 200000

输出描述

输出t行,每行输出一个整数,代表连续子数组的最大和。

样例

输入

3
5 10
5 -1 -5 -3 2
2 -3
-5 -2
6 10
4 -2 -11 -1 4 -1

输出

15
-2
15

提示

第一组询问,修改第二个数。

第二组询问,不进行任何修改。

第三组询问,修改第三个数。

思路

step1:不带修改

如果不带修改,那么就是朴素的最大子段和。经典dp

step2:带修改

某个位置改成 x x x之后,我们要考虑的是必须包含这个位置的最大子段和。那么也就是左边一段(可以为空)拼上 x x x再拼上右边一段(可以为空),如下图所示

容易发现, A A A 必须是以 a i − 1 a_{i-1} ai1 结尾的最大子段和。 B B B必须是以 a i + 1 a_{i+1} ai+1 为开头的最大子段和。

证明:反证法

A A A是以 a i a_i ai为结尾的最大子段和。

假设存在一个更大的 A ′ > A A' > A A>A由于 A ′ A' A也是以 a i a_i ai 为结尾的子段。可以推出 A A A 并不是最大子段和,和假设冲突。

所以 A A A 一定要是最大子段和

所以做法就是:正着求一遍最大子段和 d p dp dp,反着求一遍最大子段和 f f f

答案为:不修改的最大子段和 与 枚举每个位置尝试修改的最大子段和 , 两者的最大值

对于前者,答案就是 m a x ( d p ) max(dp) max(dp)

对于后者的第 i i i 个位置 , m a x ( d p ( i − 1 ) , 0 ) + x + m a x ( f ( i + 1 , 0 ) ) max(dp(i-1) , 0)+x+max(f(i+1 , 0)) max(dp(i1),0)+x+max(f(i+1,0))

注意,对 0 0 0 取最大值是代表左右两个段可以为空

代码

def read_input():
    # Read the number of test cases
    t = int(input())

    # Iterate over each test case
    test_cases = []
    for i in range(t):
        # Read the input for the current test case
        n, x = map(int, input().split())
        a = list(map(int, input().split()))
        test_cases.append((n, x, a))

    return test_cases

def calculate_dp(n, a):
    # Initialize the dp array
    dp = [0] * (n + 2)

    # Calculate the dp array
    for j in range(1, n + 1):
        dp[j] = max(dp[j - 1], 0) + a[j - 1]

    return dp

def calculate_f(n, a):
    # Initialize the f array
    f = [0] * (n + 2)

    # Calculate the f array
    for j in range(n, 0, -1):
        f[j] = max(f[j + 1], 0) + a[j - 1]

    return f

def calculate_ans(n, x, a, dp, f):
    # Initialize the answer
    ans = 0

    # Calculate the answer
    # Case 1: Do not modify any value
    ans = max(dp[1:n + 1])

    # Case 2: Modify each value and check the maximum subarray sum
    for j in range(1, n + 1):
        ans = max(ans, x + max(dp[j - 1], 0) + max(f[j + 1], 0))

    return ans

def solve_test_cases(test_cases):
    # Iterate over each test case
    for n, x, a in test_cases:
        # Calculate the dp array
        dp = calculate_dp(n, a)

        # Calculate the f array
        f = calculate_f(n, a)

        # Calculate the answer
        ans = calculate_ans(n, x, a, dp, f)

        # Print the answer for the current test case
        print(ans)

test_cases = read_input()
solve_test_cases(test_cases)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

塔子哥学算法

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值