题意:类似消消乐,给出n组键值对,每次选择键的gcd不是1的相邻的2个合并,然后可以获得这2个键对应的值和,消去的部分后,剩下的可以看做是相邻的。比如3 2 2 3消去2 2 那么3 3就是相邻的
区间dp了,因为最终值和每次抉择是有关的,贪心是不可以的,
容易想到的是对于一个区间[i,j],枚举每一次选择的位置k,然后计算出所有的可能再取最大值,当然直接暴力,时间是不够的,原因在于计算过程中的很多次的重复计算啦,所以记忆化搜索啦,对于区间长度是1的,答案就是0,区间长度是2的,检查2个数gcd,在决定,然后区间长度大于等于3的,就是暴力算啦,因为有记忆化,所以很快的。
当然区间dp ,写出状态转移方程,一般三个for进行递推,或者用记忆化搜索。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define cl(a,b) memset(a,b,sizeof(a))
#define gcd __gcd
#define LL long long
const int maxn = 305;
/*
2
4
3 2 2 3
1 2 3 4
*/
int n;
LL a[maxn],b[maxn],sum[maxn];
LL dp[maxn][maxn];
LL dfs(int i,int j){
if(i >= j) return 0;
if(-1 != dp[i][j]) return dp[i][j];
LL &ans = dp[i][j];
if(j - i == 1){
if(gcd(a[i], a[j]) == 1){
ans = 0;
}
else {
ans = b[i] + b[j];
}
}
else if(sum[j-1] - sum[i] == dfs(i+1, j-1)){
if(gcd(a[i], a[j]) == 1)
ans = dfs(i+1,j-1);
else
ans = dfs(i+1,j-1) + b[i] + b[j];
}
else
for(int k = i; k < j; k++){
ans = max(ans, dfs(i,k)+dfs(k+1,j));
}
return ans;
}
int main(){
int T;scanf("%d", &T);
while(T--){
cl(a,0);cl(b,0);
scanf("%d", &n);
for(int i = 1; i <= n; i++){
scanf("%lld", &a[i]);
}
sum[0] = 0;
for(int i = 1; i <= n; i++){
scanf("%lld", &b[i]);
sum[i] = sum[i-1] + b[i];
}
cl(dp,-1);
printf("%lld\n", dfs(1,n));
}
return 0;
}