Description
给出 n n 个数对,对于相邻的两个数对,如果它们的键值不互素则可以同时拿走这两个数对,得分为其价值之和,问最多可以得多少分
Input
第一行一整数 T T 表示用例组数,每组用例首先输入一整数表示数对的个数,之后输入 n n 个整数表示每个数对的键值,最后输入 n n 个整数表示每个数对的价值
(1≤T≤10,1≤n≤300,1≤keyi≤109,0≤vali≤109) ( 1 ≤ T ≤ 10 , 1 ≤ n ≤ 300 , 1 ≤ k e y i ≤ 10 9 , 0 ≤ v a l i ≤ 10 9 )
Output
输出最大得分
Sample Input
3
3
1 2 3
1 1 1
3
1 2 4
1 1 1
4
1 3 4 3
1 1 1 1
Sample Output
0
2
0
Solution
区间 DP D P ,以 dp[l][r] d p [ l ] [ r ] 表示从第 l l 个数对到第个数对可以拿到的最大得分,有两种方案,要么把该区间分成两部分分别考虑,要么就是第 l+1 l + 1 个数对到第 r−1 r − 1 个数对全部被拿走且 gcd(keyl,keyr)>1 g c d ( k e y l , k e y r ) > 1 ,这样第 l l 个数对和第个数对可以凑一起拿走,为快速判断是否可以将区间 [l+1,r−1] [ l + 1 , r − 1 ] 的数对全部拿走,对价值求前缀和 sumi=∑j=1ivalj s u m i = ∑ j = 1 i v a l j ,如果 dp[l+1][r−1]=sumr−1−suml d p [ l + 1 ] [ r − 1 ] = s u m r − 1 − s u m l 则说明区间 [l+1,r−1] [ l + 1 , r − 1 ] 可以全部被拿走,以此求出 dp[1][n] d p [ 1 ] [ n ] 即为答案,时间复杂度 O(Tn3) O ( T n 3 )
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
#define maxn 333
int T,n,a[maxn],v[maxn];
ll sum[maxn],dp[maxn][maxn];
int gcd(int a,int b)
{
return b?gcd(b,a%b):a;
}
ll dfs(int l,int r)
{
if(dp[l][r]!=-1)return dp[l][r];
ll ans=-1;
if(l==r)ans=0;
else if(r==l+1)
{
if(gcd(a[l],a[r])>1)ans=v[l]+v[r];
else ans=0;
}
else
{
int temp=gcd(a[l],a[r]);
if(temp!=1&&dfs(l+1,r-1)==sum[r-1]-sum[l])ans=sum[r]-sum[l-1];
for(int i=l;i<r;i++)ans=max(ans,dfs(l,i)+dfs(i+1,r));
}
return dp[l][r]=ans;
}
int main()
{
scanf("%d",&T);
while(T--)
{
memset(dp,-1,sizeof(dp));
scanf("%d",&n);
sum[0]=0;
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<=n;i++)scanf("%d",&v[i]),sum[i]=sum[i-1]+v[i];
printf("%I64d\n",dfs(1,n));
}
return 0;
}