1 条题解
-
0
C. George and Job 详细题解
问题描述
给定一个长度为 的整数序列 。需要从中选出 个互不重叠且长度均为 的区间
满足:
$$1 \le l_1 \le r_1 < l_2 \le r_2 < \dots < l_k \le r_k \le n $$且 。目标是最大化这些区间内所有元素的总和。
数据范围
问题分析
由于区间长度固定为 ,且不能重叠,相当于在原序列中选出 个长度为 的连续子段,子段之间至少间隔一个位置(因为 )。
这是一个典型的动态规划问题,可以用前缀和快速计算任意区间 的和。动态规划解法
预处理
先计算前缀和数组 ,其中
那么区间 的和为
状态定义
设 表示已经选择了 个区间,且最后一个区间的右端点不超过 时能获得的最大总和。
其中 ,。边界条件
- 对所有 (没有选任何区间,总和为 )。
- 当 时,无法选出 个不重叠的长度为 的区间,因此 (初始化时已为 )。
状态转移
对于 ,考虑位置 :
- 不选以 结尾的区间:此时最优值就是 。
- 选以 结尾的区间:该区间为 ,其和为 。那么前 个区间的右端点必须 (因为不能重叠),因此这部分的最大值为 。
总值为 。
综上,转移方程为:
$$dp[i][j] = \max\left( dp[i][j-1],\; dp[i-1][j-m] + pre[j] - pre[j-m] \right) $$答案
最终答案为 ,即选出 个区间且最后一个区间右端点不超过 的最大和。
时间复杂度与空间复杂度
- 状态数:,由于 ,最坏情况下 ,故 。当 时,,在可接受范围内。
- 每个状态转移 ,总时间复杂度 。
- 空间复杂度:使用二维数组 ,约为 字节 MB,在内存限制内( MB)。也可以优化为滚动数组降至 ,但本题二维直接实现即可。
示例验证
示例1
输入:
5 2 1 1 2 3 4 5,选择长度为 的区间,最大和区间为 和 ,输出 。
示例2
输入:
7 1 3 2 10 7 18 5 33 0,每个区间就是一个单点。选择最大的 个数:,总和 ,输出 。
代码实现(标程)
#include <bits/stdc++.h> using namespace std; int main() { int n, m, k; cin >> n >> m >> k; vector<long long> pre(n + 1, 0); for (int i = 1; i <= n; ++i) { cin >> pre[i]; pre[i] += pre[i - 1]; // 前缀和 } // dp[i][j]: 选 i 个区间,最后一个右端点 ≤ j 的最大和 vector<vector<long long>> dp(k + 1, vector<long long>(n + 1, 0)); for (int i = 1; i <= k; ++i) { for (int j = i * m; j <= n; ++j) { dp[i][j] = max(dp[i][j - 1], dp[i - 1][j - m] + (pre[j] - pre[j - m])); } } cout << dp[k][n] << endl; return 0; }总结
本题的关键在于将问题转化为选 个固定长度的不重叠子段,并利用前缀和快速计算子段和。动态规划的状态设计为“已选 个区间,最后一个区间右端点不超过 ”,通过考虑是否以 结尾进行转移,保证了无后效性和最优子结构。最终时间复杂度 ,在给定数据范围内可行。
- 1
信息
- ID
- 6486
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 6
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者