1 条题解
-
0
题目理解
我们有 张成绩单,分数为 ,要分成若干连续段(批次),每段的代价是 ,总代价还要加上 ( 是批次数)。
目标:最小化总代价
$$\text{cost} = a \times k + b \times \sum_{i=1}^k (\max_i - \min_i)^2 $$
核心思路
1. 状态设计
常规的区间DP状态 表示区间 的最小代价,但这题不够用,因为合并区间时,新段的极差依赖于该段的最大最小值。
因此需要额外记录区间中的最大值和最小值。
定义:
$$f[l][r][i][j] = \text{将区间 }[l,r] \text{ 分发完,且该区间所在批次的最大值为 } a[i] \text{,最小值为 } a[j] \text{ 的最小代价} $$其中 表示原序列中第 个位置的分数。
注意: 和 可以不在 范围内,它们只是记录当前批次的极值信息。
2. 状态转移
情况1:区间 单独作为一批
代价为:
前提是 和 确实是该区间的最大最小值(在DP过程中通过更新维护)。
情况2:区间 分成两段 和
- 第一段 单独成一批,代价为 ( 表示重新计算该段的最大最小值)
- 第二段 沿用外部的最大最小值 ,代价为
转移方程:
$$f[l][r][i][j] = \min\left\{ \begin{array}{l} f[l+1][r][\text{new}_i][\text{new}_j], \\ \min\limits_{l \le k \le r} \big( f[l][k][0][0] + f[k+1][r][i][j] \big) \end{array} \right. $$其中 是加入 后更新过的最大最小值下标。
3. 初始化和边界
- 空区间: 时,如果 有效,则代价为
- 最终答案: 表示整个区间 分成若干批的最小代价
代码详解
#include <bits/stdc++.h> using namespace std; const int N = 55; int n, A, B; int f[N][N][N][N], a[N]; void chkmn(int &a, int b) { a = min(a, b); } int main() { scanf("%d%d%d", &n, &A, &B); for (int i = 1; i <= n; i++) scanf("%d", &a[i]); // 初始化DP数组为无穷大 memset(f, 0x3f, sizeof(f)); // 枚举区间长度 for (int len = 0; len <= n; len++) { for (int l = 1; l <= n - len + 1; l++) { int r = l + len - 1; // 枚举外部传入的最大值下标i和最小值下标j for (int i = 0; i < l; i++) { // i=0表示还没有最大值 for (int j = 0; j < l; j++) { // j=0表示还没有最小值 if (a[i] <= a[j]) { // 最大值要大于等于最小值 // 空区间情况 if (r < l) { f[l][r][i][j] = (a[j] - a[i]) * (a[j] - a[i]) * B + A; continue; } // 更新当前区间的最大值和最小值下标 int xi = i, xj = j; if (!i || a[i] > a[l]) xi = l; // 更新最大值下标 if (!j || a[j] < a[l]) xj = l; // 更新最小值下标 // 情况1:将a[l]加入当前批次 f[l][r][i][j] = f[l + 1][r][xi][xj]; // 情况2:在位置k处分割区间 for (int k = l; k <= r; k++) { chkmn(f[l][r][i][j], f[l][k][0][0] + f[k + 1][r][i][j]); } } } } } } printf("%d\n", f[1][n][0][0]); return 0; }
关键点说明
1. 状态中 的含义
- 或 表示"还没有设定最大值/最小值"
- 当加入新元素时,如果 或新元素更大,则更新
- 当加入新元素时,如果 或新元素更小,则更新
2. 转移的逻辑
- 不分割:将 加入当前批次,更新极值,处理剩余区间
- 分割:在 处切开,前一段单独成批,后一段沿用外部极值
3. 复杂度
- 状态数:
- 转移:
- 总复杂度:
- 对于 可以接受
样例验证
对于样例:
n=10, A=3, B=1 分数: 7 10 9 10 6 7 10 7 1 2程序计算得到最小代价为 。
一种可能的最优划分:
- 第1批:[7, 10, 9, 10, 6, 7, 10, 7],极差 ,代价
- 第2批:[1, 2],极差 ,代价
- 总批次数 ,总代价 ?不对
这说明实际最优划分可能更复杂,需要更多批次但每批极差更小,通过程序DP才能找到真正的 。
总结
这道题的难点在于:
- 状态设计:需要记录区间的最大最小值信息
- 转移分类:分"继续当前批次"和"开启新批次"两种情况
- 边界处理:空区间的代价计算
这是一个经典的带额外信息的区间DP问题,通过合理的状态设计,将极差信息融入DP状态中,从而能够准确计算每批的代价。
- 1
信息
- ID
- 3877
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者