如何证明Codeforces问题A. Boredom对应题解的正确性?
Codeforces 455A Boredom 题解代码逻辑说明
题目规则
给定由n个整数构成的序列a,玩家可执行多步操作。单步操作中玩家可选择序列内的一个元素(记为ak)删除,此时序列中所有值等于ak+1与ak-1的元素也会被同步删除,该步操作将为玩家贡献ak点得分。求玩家可获得的最高总得分。
参考代码
你看到的用户提交代码如下:
input() z = [0] * 7**6 for i in map(int, input().split()): z[i] += i a = b = 0 for i in z: a, b = max(a, i + b), a print(a)
逻辑拆解
你已经理解了预处理部分的逻辑:
数组z的下标对应序列里可能出现的数值,z[x] = x * 序列中值为x的元素个数。这个预处理的依据很简单:如果你决定拿数值x的分数,那么所有x+1、x-1的元素都会被删除,剩下的所有x你可以全部拿走,总收益固定是z[x],不存在只拿部分x的更优选择。
核心的循环部分本质是空间优化后的动态规划,对应经典的「打家劫舍」问题模型:
- 先写无空间优化的DP状态定义:设
dp[x]为处理完0到x所有数值时,能拿到的最高得分。 - 对每个数值x,只有两种互斥的选择:
- 不拿x的分数:此时x-1拿不拿都不受影响,最高得分就是处理完x-1的最优值
dp[x-1] - 拿x的分数:此时x-1绝对不能拿,最高得分就是x的总收益加上处理完x-2的最优值
z[x] + dp[x-2]
- 不拿x的分数:此时x-1拿不拿都不受影响,最高得分就是处理完x-1的最优值
- 因此状态转移方程为:
dp[x] = max(dp[x-1], z[x] + dp[x-2])
代码里的两个变量就是用来压缩DP数组空间的:
- 遍历
z数组时是从0开始从小到大逐个数处理的,进入当前轮(处理数值i)时:- 旧值
a存储的是处理完i-1时的最优得分,也就是dp[i-1] - 旧值
b存储的是处理完i-2时的最优得分,也就是dp[i-2]
- 旧值
- 执行
a, b = max(a, i + b), a时:- 先算出当前i对应的最优值
max(a, i + b),作为新的a,供下一轮处理i+1时当dp[i]使用 - 把旧的
a(也就是dp[i-1])赋值给新的b,供处理i+2时当dp[i]使用
- 先算出当前i对应的最优值
- 遍历完所有数值后,
a存储的就是处理完全部数值后的全局最优得分,直接输出即可。
补充说明:代码初始化z数组长度为7**6(即117649),是因为题目给定的序列元素最大值不超过1e5,这个长度足够覆盖所有可能出现的数值,不会出现下标越界问题。
内容的提问来源于stack exchange,提问作者Tatai
相关产品推荐
相关产品推荐

