1 条题解

  • 0
    @ 2026-9-25 0:21:03

    upd 2025/7/12:更新了一些错别字。 题目传送门

    题意

    把 nn 个数分成 mm 个区间,使得每个数与所在区间的中位数之差的总和最小,输出这个最小值。

    思路

    先排序,明显的 DP:

    • 定义状态:fi,jf_{i,j} 表示前 ii 个区域使用 jj 种颜色的最小误差。
    • 设 color(l,r)color(l,r) 为区间 ll 到 rr 为一个颜色时的误差。我们枚举一个 kk,从 kk 到 jj 为一个新的颜色,而 fk,j−1f_{k,j-1} 为 11 到 kk 使用 j−1j-1 种颜色,把它们相加,刚好是 fi,jf_{i,j},所以我们只需要在这里求个最小值就可以了
    • 状态转移:$f_{i,j}=\begin{cases} 0 & \text{ if } i=0,j=0 \\ f_{k,j-1}+color(k+1,i) & \text{ if } i>0,j>0 \end{cases}$
    • 优化:这里每次 color(l,r)color(l,r) 需要遍历一次 ll 到 rr,于是我们可以考虑前缀和优化。细节看代码。

    代码

    记得开 long long。

    #include <bits/stdc++.h>
    using namespace std;
    int n, m, a[3010];
    long long sum[3010], f[3010][20];
    long long color(int l, int r)
    {
        int mid = (l + r) / 2;
        return a[mid] * (mid - l) - (sum[mid - 1] - sum[l - 1]) + sum[r] - sum[mid] - a[mid] * (r - mid);
    }
    int main()
    {
    	scanf("%d%d", &n, &m);
        for (int i = 1; i <= n; i++)
            scanf("%d", &a[i]);
        sort(a + 1, a + n + 1);
        for (int i = 1; i <= n; i++)
            sum[i] = sum[i - 1] + a[i];
        memset(f, 0x3f, sizeof(f)), f[0][0] = 0;
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= m; j++)
                for (int k = 0; k < i; k++)
                    f[i][j] = min(f[i][j], f[k][j - 1] + color(k + 1, i));
        printf("%lld", f[n][m]);
    	return 0;
    }
    

    给个赞再走吧。 题目传送门

    • 1

    信息

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