题解:P11444 [Code+#6] 祖玛
szh_AK_all · · 题解
好玩的区间 dp。
竟然是这题题解区的第一篇题解诶。
分析
根据套路,先设
于是设
枚举断点
-
当
a_k=a_{l-1} 且p=1 时,可以选择先将区间[l,k-1] 删完,再将区间[1,l-1] 与区间[k,r] 拼起来,答案即f_{l,k-1,0,0}+f_{k+1,r,x+1,1} 。注意此时区间[l,k-1] 是与外界隔绝的。 -
当
X_{min}\le x \le X_{max} 或者x=0 时,考虑将区间[l,r] 直接从k 处断开。那么与k 有联系的区间是[k+1,r] ,并且此时会产生x^2 的权值,答案即为f_{l,k-1,0,0}+f_{k+1,r,1,1}+x^2 。
细节:
-
答案应是所有可以转移到的
f 的最大值; -
个人认为写记忆化搜索更方便点;
-
边界情况需要特判,即
l>r 时,权值为x^2 (前提是x 没有越界); -
转移的开始可以有很多种,这里是用
f_{i,n,1,1}(2\le i\le n) 及f_{1,n,0,0} 作为转移的开始。
Code
#include <bits/stdc++.h>
using namespace std;
#define int long long
int a[105], f[105][105][105][2], mi, ma, tmp, k;
int dfs(int l, int r, int xuan, int p) {
if (l > r) {
if (xuan && (mi > xuan || xuan > ma))
return -1000000000;
tmp = max(tmp, xuan * xuan);
return xuan * xuan;
}
if (f[l][r][xuan][p] > k)
return f[l][r][xuan][p];
if (xuan > ma)
return 0;
int ans = 0;
for (int k = l; k <= r; k++)
if (p && a[k] == a[l - 1])
ans = max(ans, dfs(l, k - 1, 0, 0) + dfs(k + 1, r, xuan + 1, 1));
for (int k = l; k <= r; k++)
if ((!xuan || (mi <= xuan && xuan <= ma)))
ans = max(ans, dfs(l, k - 1, 0, 0) + dfs(k + 1, r, 1, 1) + xuan * xuan);
tmp = max(tmp, ans);
return f[l][r][xuan][p] = ans;
}
signed main() {
memset(f, -0x3f, sizeof(f));
k = f[0][0][0][0];
int n;
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i];
cin >> mi >> ma;
for (int i = 1; i <= n; i++)
dfs(i + 1, n, 1, 1);
tmp = max(tmp, dfs(1, n, 0, 0));
cout << tmp;
}