P5154 数列游戏
区间DP加求GCD。
(本片题解用更相减损法来求GCD)。
设f[l][r]为bool类型,表能否取走l位置到r位置的数。
然后转移方程分两种情况。
①取走l位置与r位置的数(前提:a[l]与a[r]不互质) 。
这就必须保证f[l+1][r-1]为true(能被取走)!
于是此方程式便能推导出来f[l][r]|=f[l+1][r-1]。
②取走i位置与i+1位置的数(前提:a[i]与a[i+1]不互质,l<i<r-1)。
这就必须保证f[l][i]与f[i+1][r]为true(能被取走)!
于是此方程式便能推导出来f[l][r]|=f[l][j]*f[j+1][r];
接下来两种做法(注:记得搞前缀和)。
①线性DP也就是俺代码用的方法(跟那些跳桩子的题差不多)。
②DFS(就像走路那样比如f[l][r]为l可以走到r)。
至于更相减损法,比辗转相除法更优,不过此题貌似不需要。
还有就是l~r中的数的个数一定是双数!(解释i+=2)
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,a[1000001];
bool f[1001][1001];
long long b[1000001],s[1000001],ans[1000001];
long long Max(long long AA,long long BB){
if(AA>BB) return AA;
return BB;
}
int gcd(int A,int B){//更相减损法
if(B>A){
int TURN=A;
A=B;
B=TURN;
}
if(A==B) return A;
if(A%2==0&&B%2==0) return 2*gcd(A/2,B/2);
if(A%2!=0&&B%2==0) return gcd(A,B/2);
if(A%2==0&&B%2!=0) return gcd(A/2,B);
return gcd(A-B,B);
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++){
scanf("%lld",&b[i]);
s[i]=s[i-1]+b[i];
}
for(int i=1;i<=n-1;i++){
if(gcd(a[i],a[i+1])!=1) f[i][i+1]=true;
else f[i][i+1]=false;
}
for(int i=3;i<=n;i+=2){
for(int l=1;l<=n-i;l++){
int r=l+i;
if(gcd(a[l],a[r])!=1) f[l][r]|=f[l+1][r-1];
for(int j=l+1;j<r-1;j++) f[l][r]|=f[l][j]*f[j+1][r];
}
}
for(int i=2;i<=n;i++){
ans[i]=ans[i-1];
for(int j=1;j<i;j++){
if(f[j][i]==false) continue;
ans[i]=Max(ans[i],ans[j-1]+s[i]-s[j-1]);
}
}
printf("%lld",ans[n]);
}