题解 好
赛时因为「不一定要全删完」坚决地否掉了区间DP,于是被打脸了
首先如果能处理出 \(f_{i, j}\) 为将区间 \([i, j]\) 全删完的最大收益,可以再加一个简单 \(n^2\) DP 得到答案
那么现在求 \(f\)
考虑最终好的序列一定是一个单峰折线(两边折线长度可以为0)
那么 \(i, j\) 可能在同一次删除也可能不同
不同的话枚举一个 \(k\) 断开即可
相同的话还需要再处理一个 \(g_{i, j}\) 为将 \([i, j]\) 删成一段上升序列的最大收益,\(h_{i, j}\) 同理但为下降序列
枚举峰顶位置,发现峰顶权值和左右两端点权值确定了折线长度就确定了,\(O(n^3)\) DP 即可
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define ll long long
//#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n;
int a[N], v[N];
namespace force{
unordered_map<int, int> mp;
int dfs(int s) {
// cout<<"s: "<<s<<endl;
if (!s) return 0;
if (mp.find(s)!=mp.end()) return mp[s];
int sta[21]; int top=0, ans=0;
for (int i=1; i<=n; ++i) if (s&(1<<(i-1))) sta[++top]=i;
for (int i=1; i<=top; ++i) {
for (int j=i; j<=top; ++j) {
int t=0;
for (int k=i; k<=j; ++k) {
if (k>i && abs(a[sta[k]]-a[sta[k-1]])!=1) goto jump;
if (k>i && k<j && a[sta[k-1]]>a[sta[k]] && a[sta[k+1]]>a[sta[k]]) goto jump;
}
for (int k=1; k<i; ++k) t|=1<<(sta[k]-1);
for (int k=j+1; k<=top; ++k) t|=1<<(sta[k]-1);
ans=max(ans, dfs(t)+v[j-i+1]);
jump: ;
}
}
return mp[s]=ans;
}
void solve() {
printf("%d\n", dfs((1<<n)-1));
}
}
namespace task1{
int mp[1<<20];
int dfs(int s) {
// cout<<"s: "<<s<<endl;
if (!s) return 0;
if (~mp[s]) return mp[s];
int sta[21], suf[22]; int top=0, ans=0, pre=0;
for (int i=1; i<=n; ++i) if (s&(1<<(i-1))) sta[++top]=i;
for (int i=1; i<=top+1; ++i) suf[i]=0;
for (int i=top; i; --i) suf[i]=suf[i+1]|(1<<(sta[i]-1));
for (int i=1; i<=top; ++i) {
if (i>1) pre|=1<<(sta[i-1]-1);
for (int j=i; j<=top; ++j) {
if (j>i && abs(a[sta[j]]-a[sta[j-1]])!=1) break;
if (j>i+1 && a[sta[j-2]]>a[sta[j-1]] && a[sta[j]]>a[sta[j-1]]) break;
ans=max(ans, dfs(pre|suf[j+1])+v[j-i+1]);
}
}
return mp[s]=ans;
}
void solve() {
memset(mp, -1, sizeof(mp));
printf("%d\n", dfs((1<<n)-1));
}
}
namespace task{
ll f[410][410], g[410][410], h[410][410], t[410];
void solve() {
memset(f, 0xc0, sizeof(f));
memset(g, 0xc0, sizeof(g));
memset(h, 0xc0, sizeof(h));
for (int i=1; i<=n; ++i) f[i][i]=v[1], g[i][i]=h[i][i]=0;
for (int i=1; i<=n; ++i) for (int j=i-1; j; --j) f[i][j]=0;
for (int l=1; l<=n; ++l) {
for (int i=1,j; i+l-1<=n; ++i) {
j=i+l-1;
for (int k=i; k<j; ++k) if (a[k]+1==a[j]) g[i][j]=max(g[i][j], g[i][k]+f[k+1][j-1]);
for (int k=i+1; k<=j; ++k) if (a[k]+1==a[i]) h[i][j]=max(h[i][j], f[i+1][k-1]+h[k][j]);
for (int k=i; k<j; ++k) f[i][j]=max(f[i][j], f[i][k]+f[k+1][j]);
for (int k=i; k<=j; ++k) if (a[k]>=a[i]&&a[k]>=a[j]) f[i][j]=max(f[i][j], g[i][k]+h[k][j]+v[2*a[k]-a[i]-a[j]+1]);
}
}
#if 0
cout<<"g: "<<endl;
for (int i=1; i<=n; ++i) {for (int j=1; j<=n; ++j) cout<<setw(20)<<g[i][j]<<' '; cout<<endl;}
cout<<"h: "<<endl;
for (int i=1; i<=n; ++i) {for (int j=1; j<=n; ++j) cout<<setw(20)<<h[i][j]<<' '; cout<<endl;}
cout<<"f: "<<endl;
for (int i=1; i<=n; ++i) {for (int j=1; j<=n; ++j) cout<<setw(20)<<f[i][j]<<' '; cout<<endl;}
#endif
for (int j=1; j<=n; ++j) {
t[j]=t[j-1];
for (int i=j; i; --i) t[j]=max(t[j], t[i-1]+f[i][j]);
}
printf("%lld\n", t[n]);
}
}
signed main()
{
freopen("good.in", "r", stdin);
freopen("good.out", "w", stdout);
n=read();
for (int i=1; i<=n; ++i) v[i]=read();
for (int i=1; i<=n; ++i) a[i]=read();
// task1::solve();
task::solve();
return 0;
}