一个关于序列的游戏——DP综合题
题目
有一个序列,你可以在上面删除符合要求的连续段若干次。每次删除都会得到连续段长度对应的分数。
需要符合的要求为:
1、相邻两个元素相差为1
2、如果某个元素不在连续段的最左或最右,那么这个元素就不能同时小于相邻的左右两个元素。
“1、2、3、4、3” “1、2” “3、2” “3”都符合条件。
显然,删除掉连续段后,这个段的左边和右边并在一起成为相邻元素。
你的任务是对于给出的序列,计算出可能获得的最大总分。
题解
首先肯定是一道区间DP,不过如果我们直接维护最大收益的话,感觉在转移时非常困难。
仔细思考可以发现,最大收益应该是由几段被完全删去的区间所拼接而成,这个拼接的过程可以转化为
设:
关于
其实本质上,一个连续段就是由两个公差为1的等差数列组成的,并且具备先递增后递减的单峰性质,而区间DP在内层本就是在枚举断点
那么转移就可以写成:
int get(int l,int r,int k){
if(a[k]+a[k]-a[l]-a[r]+1<0)return -inf;
if(a[k]<a[l]||a[k]<a[r])return -inf;
return up[l,k]+down[k,r]+val[a[k]+a[k]-a[l]-a[r]+1];
}
//在solve中
for(int len=2;len<=n;len++){
for(int l=1,r=len;r<=n;l++,r++){
for(int k=l;k<=r;k++){
f[l][r]=max(f[l][r],get(l,r,k));
if(k<r)f[l][r]=max(f[l][r],f[l][k]+f[k+1][r]);
}
}
}
那么问题就变为了如何维护
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 250
#define inf 0x3f3f3f3f
int f[N][N], up[N][N], down[N][N], ans[N][N], n, m, a[N], val[N];
int get(int l, int r, int k) {
if (a[k] + a[k] - a[l] - a[r] + 1 > n)return -inf;
if (a[k] < a[l] || a[k] < a[r])return -inf;
return up[l][k] + down[k][r] + val[a[k] + a[k] - a[l] - a[r] + 1];
}
void init() {
cin >> n;
for (int i = 1; i <= n; i++)cin >> val[i];
for (int i = 1; i <= n; i++)cin >> a[i];
}
//在solve中
void solve() {
memset(f, 0xcf, sizeof f);
memset(up, 0xcf, sizeof f);
memset(down, 0xcf, sizeof f);
for (int i = 1; i <= n; i++)f[i][i - 1] = 0;
f[n + 1][n] = 0;
for (int i = 1; i <= n; i++)up[i][i] = down[i][i] = 0;
for (int len = 0; len <= n; len++) {
for (int l = 1, r = len; r <= n; l++, r++) {
for (int k = l; k <= r; k++) {
f[l][r] = max(f[l][r], get(l, r, k));
if (k < r)f[l][r] = max(f[l][r], f[l][k] + f[k + 1][r]);
}
if (a[l - 1] == a[r + 1] + 1) {
for (int L = 1; L < l; L++) {
for (int R = r + 1; R <= n; R++) {
down[L][R] = max(down[L][R], down[L][l - 1] + f[l][r] + down[r + 1][R]);
}
}
}
if (a[l - 1] == a[r + 1] - 1) {
for (int L = 1; L < l; L++) {
for (int R = r + 1; R <= n; R++) {
up[L][R] = max(up[L][R], up[L][l - 1] + f[l][r] + up[r + 1][R]);
}
}
}
// printf("%d ", f[l][r]);
}
// puts("");
}
for (int len = 1; len <= n; len++) {
for (int l = 1, r = len; r <= n; l++, r++) {
ans[l][r] = max(ans[l][r], f[l][r]);
for (int k = l; k < r; k++) {
ans[l][r] = max(ans[l][r], ans[l][k] + ans[k + 1][r]);
}
}
}
}
int main() {
init();
solve();
printf("%d\n", ans[1][n]);
return 0;
}
说明:由于我们是用现成的
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!