Description
Solution
明显的区间dp
根据套路,设计状态:
(f[i][j]) 表示,删去区间 ([i, j]) 之间的数,能得到的最大得分。
但是我们发现,这个并不好转移,我们无法记录是否都能被删除。
所以我们转换一下思路。
设 (f[i][j]) 表示,区间 ([i, j]) 能否被完全删除。
那么我们有两种可能:
-
(a[i]) 和 (a[j]) 不互质,那么我们可以从 (f[i + 1][j - 1]) 转移过来。
-
枚举断点 (k),我们先令区间 ([i, k]) 和 区间 ([k + 1, j]) 合并,在合并区间 ([i, j])
有了判断能否删除的状态之后,我们还要统计答案。
所以设 (g[i]) 为处理前 (i) 个数之后能得到的最大得分。
那么转移方程就是:(g[i] = max(g[i], g[j - 1] + sum[i] - sum[j - 1]))
(g[i]) 初值为 (g[i - 1]),(sum[i]) 为前 (i) 个数的分数前缀和。
Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#define ll long long
using namespace std;
const ll N = 1010;
ll n;
ll a[N], b[N], f[N][N], g[N];
ll sum[N];
inline ll gcd(ll a, ll b){
return !b ? a : gcd(b, a % b);
}
signed main(){
scanf("%lld", &n);
for(ll i = 1; i <= n; i++)
scanf("%lld", &a[i]);
for(ll i = 1; i <= n; i++){
scanf("%lld", &b[i]);
sum[i] = sum[i - 1] + b[i];
}
for(ll i = 1; i < n; i++)
f[i][i + 1] = (gcd(a[i], a[i + 1]) != 1);//f 数组初值
for(ll len = 3; len <= n; len++)
for(ll i = 1; i + len - 1 <= n; i++){
ll j = i + len - 1;
if(len > 3 && gcd(a[i], a[j]) != 1) f[i][j] |= f[i + 1][j - 1];//转移 1
for(ll k = i + 1; k < j - 1; k++)
f[i][j] |= (f[i][k] & f[k + 1][j]);//转移 2
}
for(ll i = 1; i <= n; i++){
g[i] = g[i - 1];
for(ll j = 1; j <= i; j++)
if(f[j][i])
g[i] = max(g[i], g[j - 1] + sum[i] - sum[j - 1]);
}
printf("%lld
", g[n]);
return 0;
}