考虑区间 dp。设 表示当前删除了区间 ,整个区间都删除完后 后面的下一个存活点是 ,且 必须在 区间内点全部删除完后才能被删除。
这个状态看着就很奇怪,考虑这个 有什么用。设 区间中最后一个删除的是 ,则在删除 的那一刻, 区间已经全没了,她最后一个点被删的时候其右邻居一定是 但是第三项还需要 后面的下一个存活点。设这个点是 其中 那么为了让左半边删除完时 还活着,必须保证 要在 删除之后再删。所以右侧区间状态是 而左侧区间看到的第二个右侧存活点是 。
为了方便,后面记贡献 。若当前限制点满足 则因为 必须晚于其左边的所有点删除,所以最后整个区间删除的 必须满足 。因此考虑枚举 得到转移方程 。
然后还有另一种情况是 也就是整个 都需要在外面的 之前删除即没有任何额外限制,此时 可以在 范围内任意取值,但是左区间必须保证 一直活到左侧区间删完,所以左状态需要使用限制点 ,转移方程形如 。
可能需要处理一下边界。
时间复杂度为 ,但是常数特别小所以可以通过该题。
:::success[Code]
namespace lowspeed_song {
inline void init() {
}
mt19937 mt(time(0));
int a[N], x[N], y[N], z[N], f[62][62][62][62];
inline int C(int l, int r, int i, int k) {
return (a[l - 1] - x[k]) * (a[l - 1] - x[k]) + (a[k] - y[r + 1]) * (a[k] - y[r + 1]) + (a[r + 1] - z[i]) * (a[r + 1] - z[i]);
}
inline void sol([[maybe_unused]]int __testcase_id, [[maybe_unused]]int __testcase_sum) {
int n; cin >> n;
for (int i = 1; i <= n; ++i) cin >> a[i];
for (int i = 1; i <= n; ++i) cin >> x[i];
for (int i = 1; i <= n; ++i) cin >> y[i];
for (int i = 1; i <= n; ++i) cin >> z[i];
for (int i = 1; i <= n; ++i)
for (int j = i + 2; j <= n + 2; ++j)
f[i][i][j][i] = f[i][i][j][i + 1] = C(i, i, j, i);
for (int len = 2; len <= n; ++len)
for (int l = 1, r = len; r <= n; ++l, ++r)
for (int i = r + 2; i <= n + 2; ++i) {
for (int j = l; j <= r; ++j)
for (int k = j; k <= r; ++k)
for (int p = k + 1; p <= r + 1; ++p)
f[l][r][i][j] = max(f[l][r][i][j], f[l][k - 1][p][j] + f[k + 1][r][i][p] + C(l, r, i, k));
for (int k = l; k <= r; ++k)
for (int p = k + 1; p <= r + 1; ++p)
f[l][r][i][r + 1] = max(f[l][r][i][r + 1], f[l][k - 1][p][k] + f[k + 1][r][i][p] + C(l, r, i, k));
}
int res = 0;
for (int i = 1; i <= n + 2; ++i)
for (int j = 1; j <= n + 2; ++j) res = max(res, f[1][n][i][j]);
cout << res << '\n';
}
} // namespace lowspeed_song:::
喜欢的话,留下你的评论吧~