[THUSC2016]成绩单

题目

  有连续的\(n\)份成绩单,每份有一个分数\(w_i\)。现在要按照批次发放成绩单,每次选择连续的一段成绩单发放,发放完一轮后将左右两端剩余的合并继续重复操作。定义\(k\)为发放次数,\(max_i\)\(min_i\)表示每次发放的分数最大值和最小值,以及两个系数\(a\)\(b\),要求最小化:

\[a\times k+b\times\sum_{i=1}^k(max_i-min_i)^2 \]

  数据范围:\(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\),转移为

\[f_{l,r-1,x,y}\xrightarrow{0}f_{l,r,\min\{x,w_r\},\max\{y,w_r\}} \]

  (2)挖它,则\(r\)一定会在其中一个窟窿内,这个窟窿一定为\([k,r],k\in[l,r]\),代价为\(g_{k,r}\),即

\[f_{l,k-1,x,y}\xrightarrow{g_{k,r}}f_{l,r,x,y} \]

  而由以上又可以得出:\(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;
}
posted @ 2020-03-08 13:51  AC-Evil  阅读(234)  评论(0编辑  收藏  举报