*题解:P11444 [Code+#6] 祖玛

题目链接

解析

首先有一个想法是设 \(f_{l,r}\) 表示删掉了区间 \([l,r]\) 所能得到的最大贡献,\(g_{i}\) 表示右端点不超过 \(i\) 的不交区间的最大贡献和。但是这样有个问题,例如 1 2 1 2 1,假设此时所有长度均可删,那么最优方案应当是先把两个 \(2\) 分别删掉总共做 \(2\) 的贡献,再把三个 \(1\) 一起删掉做 \(3^2 = 9\) 的贡献。但是显然目前的 \(f\) 定义没有考虑到这种情况,要删就把区间内的全都删了。

故更改状态,设 \(f_{l,r,i,j}\) 表示考虑区间 \([l,r]\),留下 \(j\) 个 \(i\) 所能得到的最大贡献。只用考虑一种保留的数是因为如果保留了两种或以上的数,且这些数都要进一步合并来对答案做贡献,那么这些数最左出现位置与最右出现位置组成的区间必定不存在交集,否则必定有部分保留下来没意义。既然没有交集,那么就必定可以按交界点切割划分为只需保留一种数的子问题。

继续优化,第一、二、四维不好去掉,能动的只有第三维。我们钦定最终保留下来的为 \(a_l\),就可以删掉第三维。正确性考虑最终保留下来的连续段的最左侧出现位置 \(l\),有 \(f_{l,r,j} = \max(f_{l + 1,l' - 1,0} + f_{l',r,j - 1})\) 其中 \(l'\) 为被保留的次左侧出现位置。枚举次左侧出现位置,即可覆盖所有情况。注意对 \(j = 1\) 的情况特殊处理,因为此时不存在次左侧出现位置。

转移方程见代码,时间复杂度 \(O(n ^ 4)\)。

总结

考虑把钦定的过程放到状态里。

代码

#include <bits/stdc++.h>
#define ls(x) ((x) << 1)
#define rs(x) (((x) << 1) | 1)
#define mid ((l + r) >> 1)
using namespace std;
const int N = 100 + 5,M = 35;
typedef pair<int,int> pii;
typedef long long ll;
typedef unsigned ui;
int a[N];
ll f[N][N][N],g[N];
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout); 
	int n,mn,mx;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];	
	}
	for(int len=1;len<=n;len++){
		for(int l=1;l + len - 1 <= n;l++){
			int r = l + len - 1;
			for(int j=0;j<=n;j++){
				f[l][r][j] = -1e18;
			}
			
		}
	}
	cin>>mn>>mx;
	for(int i=1;i<=n;i++){
		if(mn <= 1)
			f[i][i][0] = 1;
		f[i][i][1] = 0; 
	}
	for(int len=2;len<=n;len++){
		for(int l=1;l + len - 1 <= n;l++){
			int r = l + len - 1;
			for(int i=l + 1;i <= r;i++)if(a[l] == a[i]){
				for(int j=1;j<=r - l + 1;j++){
					if(f[l + 1][i - 1][0] >= 0){
						f[l][r][j] = max(f[l][r][j],f[l + 1][i - 1][0] + f[i][r][j - 1]);
						
					}
				}
			}
			f[l][r][1] = max(f[l][r][1],f[l + 1][r][0]);
			for(int i=mn;i<=min(r - l + 1,mx);i++){
				if(f[l][r][i] >= 0)
					f[l][r][0] = max(f[l][r][0],f[l][r][i] + 1ll * i * i);
			}
		}
	}
	for(int r=1;r<=n;r++){
		g[r] = g[r - 1];
		for(int len=1;r - len + 1 >= 1;len++){
			int l = r - len + 1;
			if(f[l][r][0] >= 0)
				g[r] = max(g[r],f[l][r][0] + g[l - 1]);
		}
	}
	cout<<g[n];
	return 0;
}
posted @ 2026-11-02 19:11  yutar  阅读(6)  评论(0)    收藏  举报