1 条题解

  • 0
    @ 2025-10-23 16:06:02

    题目理解

    我们有 nn 张成绩单,分数为 w1,w2,,wnw_1, w_2, \dots, w_n,要分成若干连续段(批次),每段的代价是 b×(maxmin)2b \times (\max - \min)^2,总代价还要加上 a×ka \times kkk 是批次数)。

    目标:最小化总代价

    $$\text{cost} = a \times k + b \times \sum_{i=1}^k (\max_i - \min_i)^2 $$

    核心思路

    1. 状态设计

    常规的区间DP状态 dp[l][r]dp[l][r] 表示区间 [l,r][l,r] 的最小代价,但这题不够用,因为合并区间时,新段的极差依赖于该段的最大最小值。

    因此需要额外记录区间中的最大值最小值

    定义:

    $$f[l][r][i][j] = \text{将区间 }[l,r] \text{ 分发完,且该区间所在批次的最大值为 } a[i] \text{,最小值为 } a[j] \text{ 的最小代价} $$

    其中 a[i]a[i] 表示原序列中第 ii 个位置的分数。

    注意iijj 可以不在 [l,r][l,r] 范围内,它们只是记录当前批次的极值信息。


    2. 状态转移

    情况1:区间 [l,r][l,r] 单独作为一批

    代价为:

    A+B×(a[i]a[j])2A + B \times (a[i] - a[j])^2

    前提是 a[i]a[i]a[j]a[j] 确实是该区间的最大最小值(在DP过程中通过更新维护)。

    情况2:区间 [l,r][l,r] 分成两段 [l,k][l,k][k+1,r][k+1,r]

    • 第一段 [l,k][l,k] 单独成一批,代价为 f[l][k][0][0]f[l][k][0][0]00 表示重新计算该段的最大最小值)
    • 第二段 [k+1,r][k+1,r] 沿用外部的最大最小值 a[i],a[j]a[i], a[j],代价为 f[k+1][r][i][j]f[k+1][r][i][j]

    转移方程:

    $$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. $$

    其中 newi,newj\text{new}_i, \text{new}_j 是加入 a[l]a[l] 后更新过的最大最小值下标。


    3. 初始化和边界

    • 空区间:l>rl > r 时,如果 i,ji,j 有效,则代价为 A+B×(a[i]a[j])2A + B \times (a[i]-a[j])^2
    • 最终答案:f[1][n][0][0]f[1][n][0][0] 表示整个区间 [1,n][1,n] 分成若干批的最小代价

    代码详解

    #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. 状态中 i,ji,j 的含义

    • i=0i=0j=0j=0 表示"还没有设定最大值/最小值"
    • 当加入新元素时,如果 i=0i=0 或新元素更大,则更新 ii
    • 当加入新元素时,如果 j=0j=0 或新元素更小,则更新 jj

    2. 转移的逻辑

    • 不分割:将 a[l]a[l] 加入当前批次,更新极值,处理剩余区间
    • 分割:在 kk 处切开,前一段单独成批,后一段沿用外部极值

    3. 复杂度

    • 状态数:O(n4)O(n^4)
    • 转移:O(n)O(n)
    • 总复杂度:O(n5)O(n^5)
    • 对于 n50n \le 50 可以接受

    样例验证

    对于样例:

    n=10, A=3, B=1
    分数: 7 10 9 10 6 7 10 7 1 2
    

    程序计算得到最小代价为 1515

    一种可能的最优划分:

    • 第1批:[7, 10, 9, 10, 6, 7, 10, 7],极差 106=410-6=4,代价 1616
    • 第2批:[1, 2],极差 11,代价 11
    • 总批次数 22,总代价 3×2+1×(16+1)=6+17=233\times 2 + 1\times (16+1) = 6+17=23?不对

    这说明实际最优划分可能更复杂,需要更多批次但每批极差更小,通过程序DP才能找到真正的 1515


    总结

    这道题的难点在于:

    1. 状态设计:需要记录区间的最大最小值信息
    2. 转移分类:分"继续当前批次"和"开启新批次"两种情况
    3. 边界处理:空区间的代价计算

    这是一个经典的带额外信息的区间DP问题,通过合理的状态设计,将极差信息融入DP状态中,从而能够准确计算每批的代价。

    • 1

    信息

    ID
    3877
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者