📅  最后修改于: 2023-12-03 15:07:09.788000             🧑  作者: Mango
给定一个长度为 $n$ 的正整数序列 $a$,你可以对它进行若干次以下操作:
在序列中选定一个长度不小于 $k$ 的连续子序列 $b$,对子序列进行如下处理:
现在,请你找到一种操作方案,最大化最终得到的权值和。
第一行四个正整数 $n,k,x,m$,含义如上所述。
第二行 $n$ 个正整数,表示原始序列。
输出一个整数,表示能够得到的最大权值和。
5 2 5 5
5 5 5 5 5
25
我们可以先通过贪心的策略,每次找到当前还没有分配过数字的最长连续子序列,并为其中每个元素分配当前最大的数字。这样,只要每次处理的子序列长度都不小于 $k$,就可以保证对于同一连续子序列中的元素,它们被分配到的数字不相同。
为了尽可能地让每个连续子序列的数字权值和最大,我们可以通过二分和前缀和算法,将每个数字权值 $w(d)$ 转化为一个相应的权重值 $s(d)$,使得对于任意两个数字绝对值相等的元素 $d_1$ 和 $d_2$,它们的权重值 $s(d_1)$ 和 $s(d_2)$ 相等。这样,我们可以使用动态规划技巧来求取连续子序列的最大权值和。
具体实现见下面的 Python 代码:
n, k, x, m = map(int, input().split())
a = list(map(int, input().split()))
# 预处理每个数字的权重值
t = {}
cnt = 0
for i in range(n):
if a[i] not in t:
cnt += 1
t[a[i]] = cnt
a[i] = t[a[i]]
# 求取前缀和
pre = [0] * (cnt + 1)
for i in range(n):
pre[a[i]] = pre[a[i] - 1] + 1
# 初始化动态规划状态
f = [[-1e18] * (cnt + 1) for _ in range(k)]
for i in range(k):
for j in range(i, cnt + 1):
f[i][j] = pre[j] - pre[0]
# 动态规划转移
for i in range(k, n):
for j in range(cnt + 1):
f[i % k][j] = -1e18
pre_min = [pre[0]] * (cnt + 1)
for j in range(1, cnt + 1):
pre_min[j] = min(pre_min[j - 1], pre[j])
for j in range(i - k + 1, i + 1):
for l in range(a[j], cnt + 1):
if f[i % k - 1][l - 1] >= 0:
f[i % k][l] = max(f[i % k][l], f[i % k - 1][l - 1] + pre[l] - pre_min[l - 1])
res = -1e18
for i in range(k, cnt + 1):
res = max(res, f[(n - 1) % k][i])
res *= x
print(res)
上面代码的时间复杂度是 $O(nk\log n)$,其中需要额外用到 $O(n)$ 辅助空间。
具体实现中可能有一些小细节,如果你对某一部分不太理解,可以参考下面的注释或留言。