带权选数
- 难度 普及−
- 时限 1s
- 空限 256m
- 线性DP
数据规模:,。
思路
按原相对顺序恰好选 个数,第 个被选中的数贡献 ,是经典的线性 DP。
设 表示处理完当前前缀、共选了 个数时的最大总分。扫描到 时有两种决策:
- 不选 : 保持不变;
- 选 :它成为第 个被选中的数,由 转移,得到 。
转移方程为 。内层让 从 倒序枚举到 ,这样 仍是上一轮的值,一维数组即可完成滚动更新,无需记录位置维度。
由于 可以为负,初值把 全部置为 (取 ,加上转移量 也不会溢出),只有 。因为 ,合法方案必然存在,最终答案即 。
复杂度
时间 ,额外空间 。答案最大约为 ,Int64 足够容纳。
仓颉实现
查看仓颉实现
import std.env.*
import std.convert.*
main() {
let reader = getStdIn()
let nm = reader.readln().getOrThrow().split(" ", removeEmpty: true).map({ p: String => Int64.parse(p) })
let n = nm[0]
let m = nm[1]
let a = reader.readln().getOrThrow().split(" ", removeEmpty: true).map({ p: String => Int64.parse(p) })
let NEG: Int64 = -4000000000000000000
let dp = Array<Int64>(m + 1, { _ => NEG })
dp[0] = 0
var i: Int64 = 0
while (i < n) {
var j = m
while (j >= 1) {
let cand = dp[j - 1] + j * a[i]
if (cand > dp[j]) {
dp[j] = cand
}
j -= 1
}
i += 1
}
println(dp[m])
}