📜  前缀和前缀后给定元素的最大总和增加子序列必须(1)

📅  最后修改于: 2023-12-03 15:22:45.576000             🧑  作者: Mango

前缀和及增加子序列
前缀和

在计算机科学和数学中,前缀和是一种特殊的累加函数。前缀和是指对于一个序列,计算从这个序列的第一个数开始到每个位置处的数的和所形成的序列。

代码示例

n = int(input())           # 输入序列长度
a = list(map(int, input().split()))     # 输入序列
p = [0]                      # 前缀和列表
for i in range(n):          
    p.append(p[-1] + a[i])   # 计算前缀和

print('前缀和列表: ', p)
前缀和的应用:最大总和增加子序列

最大总和增加子序列问题是指在一个整数序列中,找到由若干个数构成的子序列,要求这个子序列中的每个数都大于序列中前面的数,并且子序列所有数的和最大。

算法思路

对于任意一个位置 $i$,假设 $dp[i]$ 表示以 $a[i]$ 结尾的最大总和增加子序列的和,则有以下递推式: $$ dp[i] = \max\limits_{j<i, a[j]<a[i]} dp[j] + a[i] \quad (a[j] < a[i]) $$ 其中,$\max\limits_{j<i, a[j]<a[i]} dp[j] + a[i]$ 表示以 $j$ 为结尾的最大总和增加子序列加上 $a[i]$,形成以 $i$ 结尾的新的最大总和增加子序列。

代码示例

n = int(input())      # 输入序列长度
a = list(map(int, input().split()))   # 输入序列
dp = a.copy()         # 初始化 dp 列表
pre = list(range(n))  # 记录每个状态的前驱节点
for i in range(n):
    for j in range(i):
        if a[j] < a[i] and dp[j] + a[i] >= dp[i]:   # 更新状态
            dp[i] = dp[j] + a[i]
            pre[i] = j   # 记录前驱节点
ans = max(dp)          # 最终答案
pos = dp.index(ans)    # 最大子序列结尾位置
s = []                 # 找到最大子序列
while pos != pre[pos]: # 从子序列结尾位置向前搜索
    s.append(a[pos])   # 添加当前节点值到子序列
    pos = pre[pos]     # 更新当前节点
s.append(a[pos])       # 添加第一个节点到子序列
s.reverse()            # 将子序列翻转回正向
print('最大总和增加子序列: ', s) 

改进

上述算法时间复杂度为 $O(n^2)$,可以通过使用前缀和来将时间复杂度降为 $O(n\log_2n)$。

首先,对于每个下标 $i$,分别计算 $a[0:i]$ 的前缀和数组 $p$。

然后,按照 $p$ 的值对 $dp$ 和 $pre$ 列表进行排序并更新。

代码示例

n = int(input())      # 输入序列长度
a = list(map(int, input().split()))   # 输入序列

p = [0]                # 计算前缀和
for i in range(n):
    p.append(p[-1] + a[i])

dp = [a[0]] + [-1] * (n - 1)  # 初始化 dp 和 pre 列表
pre = list(range(n))
for i in range(1, n):
    x = p[i - 1]            # 计算排序的数组
    idx = sorted(range(i), key=lambda j: dp[j] - p[j], reverse=True)
    for j in idx:
        if a[j] < a[i] and dp[j] + a[i] >= dp[i]:
            dp[i] = dp[j] + a[i]
            pre[i] = j

ans = max(dp)          # 最终答案
pos = dp.index(ans)    # 最大子序列结尾位置
s = []                 # 找到最大子序列
while pos != pre[pos]: # 从子序列结尾位置向前搜索
    s.append(a[pos])   # 添加当前节点值到子序列
    pos = pre[pos]     # 更新当前节点
s.append(a[pos])       # 添加第一个节点到子序列
s.reverse()            # 将子序列翻转回正向
print('最大总和增加子序列: ', s) 

以上就是前缀和及最大总和增加子序列的介绍和实现,可以通过前缀和和动态规划的思路解决此类问题。