题解 好

传送门

赛时因为「不一定要全删完」坚决地否掉了区间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;
}
posted @ 2022-02-12 20:46  Administrator-09  阅读(0)  评论(0编辑  收藏  举报