[lnsyoj2239/luoguP5336/THUSC2016]成绩单
题意
给定序列 \(W\) 及常数 \(a,b\),对其进行删除操作,每次可以删除相邻的一段 \([L,R]\),然后原序列中的 \(L-1\) 与 \(R+1\) 拼合起来,视为相邻。记共操作 \(k\) 次,每次操作中,该段的最大值为 \(mx_i\),最小值为 \(mn_i\),求
\[a \times k + b \times \sum_{i = 1} ^ k (mx_i - mn_i) ^ 2
\]
的最小值。
赛时 10PTS
赛后
本题需要处理相邻段问题,因此可以用 DP 解决。
记 \(f_{l,r}\) 表示将 \([L,R]\) 删完后的最小值。由于存在拼合这样的变态要求,因而无法直接解决。我们继续考虑,可以记录将 \([L,R]\) 删完的前一步 \(g\)。由于代价只与 \(mx\) 和 \(mn\) 有关,因此我们记录四维数组 \(g_{l,r,mn,mx}\) 表示 \([L,R]\) 删完之前,剩余的元素中最大值为 \(mx\),最小值为 \(mn\) 的最终结果的最小值。因此就有:
\[f_{l,r} = \min\{g_{l,r,mn,mx} + a + b \times (mx-mn)^2\}
\]
我们考虑 \(g\),当将 \(r\) 向右移动一位时,我们发现有两种情况:
- 在最后一步时才删除 \(W_r\);
- 在最后一步之前删除 \(W_r\)。
若为情况 \(1\),则我们需要进行刷表转移,即:
\[g_{l,r + 1,\min\{mn,W_{r+1}\},\max\{mx,W_{r+1}\}}=\min\{g_{l,r + 1,\min\{mn,W_{r+1}\},\max\{mx,W_{r+1}\}},g_{l,r,mn,mx}\}
\]
否则,我们进行填表转移,此时我们需要枚举一个断点 \(t\),使得 \((t, r]\) 被一起删除,即:
\[g_{l,r,mn,mx}=\min_{i=l}^{r-1}\{g_{l,t,mn,mx}+f_{t+1,r}\}
\]
由于 \(w\) 范围较大,因此需要离散化。
代码
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
const int N = 55;
vector<int> ha;
int f[N][N], g[N][N][N][N];
int n, a, b, w[N];
int main(){
scanf("%d%d%d", &n, &a, &b);
for (int i = 1; i <= n; i ++ ) scanf("%d", &w[i]), ha.push_back(w[i]);
sort(ha.begin(), ha.end());
ha.erase(unique(ha.begin(), ha.end()), ha.end());
for (int i = 1; i <= n; i ++ )
for (int j = 0; j < ha.size(); j ++ )
if (w[i] == ha[j]){
w[i] = j;
break;
}
memset(f, 0x3f, sizeof f), memset(g, 0x3f, sizeof g);
for (int i = 1; i <= n; i ++ ) g[i][i][w[i]][w[i]] = 0;
for (int len = 1; len <= n; len ++ ){
for (int l = 1; l + len - 1 <= n; l ++ ){
int r = l + len - 1;
for (int mn = 0; mn < ha.size(); mn ++ ){
for (int mx = 0; mx < ha.size(); mx ++ ){
for (int t = l; t < r; t ++ ){
g[l][r][mn][mx] = min(g[l][r][mn][mx], g[l][t][mn][mx] + f[t + 1][r]);
}
g[l][r + 1][min(mn, w[r + 1])][max(mx, w[r + 1])] = min(g[l][r + 1][min(mn, w[r + 1])][max(mx, w[r + 1])], g[l][r][mn][mx]);
f[l][r] = min(f[l][r], g[l][r][mn][mx] + a + b * (ha[mx] - ha[mn]) * (ha[mx] - ha[mn]));
}
}
}
}
printf("%d\n", f[1][n]);
return 0;
}