[THUSC2016]成绩单
题目
有连续的\(n\)份成绩单,每份有一个分数\(w_i\)。现在要按照批次发放成绩单,每次选择连续的一段成绩单发放,发放完一轮后将左右两端剩余的合并继续重复操作。定义\(k\)为发放次数,\(max_i\)和\(min_i\)表示每次发放的分数最大值和最小值,以及两个系数\(a\)和\(b\),要求最小化:
数据范围:\(n\leq 50\),\(1\leq w_i\leq 1000\)。
题解
是一道不错的 DP 题。显然跟区间有关,复杂度估计在\(\mathcal O(n^5)\)左右吧。
我们每次发放操作相当于在区间上挖了一个“窟窿”,经过若干次操作挖了一连串的“窟窿”,然后再一次操作经过许多“窟窿”,把剩下的选走。
所以我们设计一个状态\(f_{l,r}\)表示\(l\)到\(r\)内,挖了若干“窟窿”的最优解,用\(g_{l,r}\)表示全部取走\(l\)到\(r\)的最优解。但这个窟窿怎么挖呢?挖了的窟窿会给状态\(f\)带来什么影响呢?其中最大的影响莫过于剩余的数的\(max_i\)和\(min_i\)了。所以我们还需要增加两维\(x\)和\(y\)表示\(l\)到\(r\)挖了若干“窟窿”后剩余的最大值为\(x\),最小值为\(y\)(由于\(n\)很小,我们将\(w_i\)离散化)。而\(f_{l,r,x,y}\),肯定要往两边(这里选右边)拓展,对于\(f_{l,r-1,x,y}\),考虑将\(r\)纳入,窝们有两种选择:
(1)不挖它,代价为\(0\),转移为
(2)挖它,则\(r\)一定会在其中一个窟窿内,这个窟窿一定为\([k,r],k\in[l,r]\),代价为\(g_{k,r}\),即
而由以上又可以得出:\(g_{l,r}=\min\limits_{x\leqslant y}\{f_{l,r,x,y}+a+b\times(val_y-val_x)^2\}\)。
初始情况为\(f_{i,i,w_i,w_i}=0\)以及\(g_{i,i}=a\),其它的为\(\infty\)。枚举\(f\)的每一维以及\(k\)的选取,故时间复杂度为\(\mathcal O(n^4)\times\mathcal O(n)=\mathcal O(n^5)\),可以通过。
代码
#include <bits/stdc++.h>
#define min(a, b) (a) < (b) ? (a) : (b)
#define max(a, b) (a) > (b) ? (a) : (b)
#define rep(i, a, b) for (int i = a, i##end = b; i <= i##end; ++i)
#define chkmax(a, b) a = max(a, b)
#define chkmin(a, b) a = min(a, b)
const int maxn = 55;
int n, a, b, w[maxn], disc[maxn];
int f[maxn][maxn][maxn][maxn], g[maxn][maxn];
inline int sqr(int x) { return x * x; }
int main() {
n = read(), a = read(), b = read();
rep(i, 1, n) disc[i] = w[i] = read();
// 离散化
std::sort(disc+1, disc+n+1);
rep(i, 1, n) w[i] = std::lower_bound(disc+1, disc+n+1, w[i]) - disc;
// 初始化
memset(f, 0x3f, sizeof f), memset(g, 0x3f, sizeof g);
rep(i, 1, n) f[i][i][w[i]][w[i]] = 0, g[i][i] = a;
// 做DP
rep(len, 1, n) // 枚举区间长度
rep(l, 1, n-len+1) {
int r = l+len-1; // 区间[l,r]
rep(x, 1, n)
rep(y, x, n) {
chkmin(f[l][r][min(x, w[r])][max(y, w[r])], f[l][r-1][x][y]); // 情况1
rep(k, l, r) chkmin(f[l][r][x][y], f[l][k-1][x][y] + g[k][r]); // 情况2
}
rep(x, 1, n)
rep(y, x, n)
chkmin(g[l][r], f[l][r][x][y] + a + b * sqr(disc[y] - disc[x])); // 维护g[l][r]
}
printf("%d", g[1][n]); // 答案为g[1][n]
return 0;
}