📌  相关文章
📜  分配给数字连续和不同数组元素的子序列的最大分数(1)

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

分配给数字连续和不同数组元素的子序列的最大分数
问题描述

给定一个长度为 $n$ 的正整数序列 $a$,你可以对它进行若干次以下操作:

在序列中选定一个长度不小于 $k$ 的连续子序列 $b$,对子序列进行如下处理:

  • 将选出的子序列 $b$ 中的相等元素去重,得到新的子序列 $c$。
  • 对新的子序列中的每个元素 $d$ 赋予一个数字权值 $w(d)$,得到权值和 $\sum_{d\in c}w(d)$。

现在,请你找到一种操作方案,最大化最终得到的权值和。

输入格式

第一行四个正整数 $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)$ 辅助空间。

具体实现中可能有一些小细节,如果你对某一部分不太理解,可以参考下面的注释或留言。