dp[i][k] 表示区间[i , k]的最大价值;dp[0][n-1]即是答案。
显然可以有两种转移:
1)和左边/右边的结合
2)中间的拿完之后两边的结合
对于第一种很简单:枚举一个j,dp[i][k] = max(dp[i][k] , dp[i][j] + dp[j+1][k]) 即可;
关键是要怎样判断表示出中间已经拿完了的状态,即对于区间[i,k]来说,如何知道区间[i+1,k-1]是否都已经拿完。
比赛的时候队友想了一种比较复杂的方式,设了两个辅助数组用DP来实现的。
看了题解之后明白,这题有一个很好的性质,
就是如果区间[i,k]是可以全部取完的,那么一定有 dp[i][k] = val[i]+....+val[k];
利用这个性质,判断[i +1,k-1]内是否全部取完了只要判断
sum[k - 1] -sum[i] =? dp[i+1][k-1] 即可。
【代码】
/* ***********************************************
Author :angon
************************************************ */
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <stack>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <time.h>
using namespace std;
#define showtime fprintf(stderr,"time = %.15f\n",clock() / (double)CLOCKS_PER_SEC)
#define lld %I64d
#define REP(i,k,n) for(int i=k;i<n;i++)
#define REPP(i,k,n) for(int i=k;i<=n;i++)
#define scan(d) scanf("%d",&d)
#define scanl(d) scanf("%I64d",&d)
#define scann(n,m) scanf("%d%d",&n,&m)
#define scannl(n,m) scanf("%I64d%I64d",&n,&m)
#define mst(a,k) memset(a,k,sizeof(a))
#define LL long long
#define N 305
#define mod 1000000007
inline int read(){int s=0;char ch=getchar();for(; ch<'0'||ch>'9'; ch=getchar());for(; ch>='0'&&ch<='9'; ch=getchar())s=s*10+ch-'0';return s;}
LL dp[N][N],sum[N];
int a[N][N];
int key[N],val[N];
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
int t;scan(t);
while(t--)
{
int n; scan(n);
REP(i,0,n) scan(key[i]);
mst(sum,0);
REP(i,0,n)
{
scan(val[i]);
sum[i]=sum[i-1]+val[i];
}
mst(a,0);
REP(i,0,n)
REP(j,0,n)
if(__gcd(key[i],key[j])!=1)
a[i][j] = 1;
mst(dp,0);
REP(i,0,n-1)
if(a[i][i+1])
dp[i][i+1] = val[i] + val[i+1];
REPP(l,2,n) //枚举区间长度
{
REP(i,0,n-l+1)
{
int k = i+l-1;
if(a[i][k] && sum[k-1]-sum[i] == dp[i+1][k-1])
{
dp[i][k] = val[i] + val[k] + dp[i+1][k-1];
//printf("dp[%d][%d] = %d\n",i,k,dp[i][k]);
continue;
}
REP(j,i,k)
{
dp[i][k] = max(dp[i][k], dp[i][j]+dp[j+1][k]);
}
//printf("dp[%d][%d] = %d\n",i,k,dp[i][k]);
}
}
printf("%I64d\n",dp[0][n-1]);
}
return 0;
}