题目背景
此题为改编题,特别鸣谢倪星宇同学。
有一次,HKE和LJC在玩一个游戏。
题目描述
游戏的规则是这样的:LJC在纸上写下两个长度均为N的数列A和B,两个数列一一对应。HKE每次可以找两个相邻的数A[i]和A[i+1],如果它们两个不互质,HKE可以选择得到(B[i]+B[i+1])分,然后擦掉A和B位置上的第i,i+1个数,并把两个序列重新按顺序编号。当所有相邻的数互质时,游戏结束。
HKE想知道他最大得分是多少。
输入输出格式
输入格式:
第1 行一个整数N;
第2 行N 个整数,依次表示Ai;
第3 行N 个整数,依次表示Bi。
输出格式:
仅含一个整数,表示B 列被删去的可能最大和。
输入输出样例
说明
对于30%的数据,N ≤ 20;
对于60%的数据,N ≤ 100;
对于80%的数据,N ≤ 500
对于100%的数据,N ≤ 800, 1 ≤ Ai, Bi ≤ 10^9。
题解:
区间dp,设dp[i][j]表示i和j之间不一定要消完的最大收益,g[i][j]为i到j必须消完的收益,那么g[i][j]有两个转移,一个是枚举断点,g[i][j]=max(g[i][j],g[i][k]+g[k+1][j]);然后我们可以消去中间的,再消i和j,
g[i][j]=max(g[i][j],c[i]+c[j]+g[i+1][j-1]);
然后是dp[i][j],如果i和k可以消,那么我们把i~k之间的都消完,然后消k+1~j。 dp[l][r]=max(dp[l][r],c[l]+c[r]+g[l+1][r-1]);如果i,j可以消,那么可以先消i,j之间的,然后消i和j。dp[l][r]=max(dp[l][r],c[l]+c[r]+g[l+1][r-1]);
代码:
#include<iostream> #include<algorithm> #include<cstring> #include<stdio.h> #include<cmath> #define ll long long #define MAXN 810 using namespace std; int n; ll a[MAXN],c[MAXN]; ll dp[MAXN][MAXN],b[MAXN][MAXN],g[MAXN][MAXN]; ll gcd(ll x,ll y){ if(y==0) return x; else return gcd(y,x%y); } bool emt(int i,int j){ if(gcd(a[i],a[j])!=1) return 1; return 0; } ll DP(int l,int r){ if(l==r) return 0; if(b[l][r]) return dp[l][r]; if(r==l+1) return max((ll)0,g[l][r]); b[l][r]=1; dp[l][r]=max((ll)0,DP(l+1,r)); for(int k=l+1;k<r;k++){ if(emt(l,k)){ if(k==l+1) dp[l][r]=max(dp[l][r],c[l]+c[k]+DP(k+1,r)); else dp[l][r]=max(dp[l][r],c[l]+c[k]+g[l+1][k-1]+DP(k+1,r)); } } if(emt(l,r)) dp[l][r]=max(dp[l][r],c[l]+c[r]+g[l+1][r-1]); return dp[l][r]; } int main(){ cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; for(int j=1;j<=n;j++) cin>>c[j]; memset(g,-37,sizeof(g)); for(int len=2;len<=n;len++){ for(int i=1;i+len-1<=n;i++){ int j=i+len-1; if(j==i+1){ if(emt(i,j)) g[i][j]=c[i]+c[j]; else g[i][j]=g[0][0]; } else{ if(emt(i,j)) g[i][j]=max(g[i][j],c[i]+c[j]+g[i+1][j-1]); for(int k=i+1;k<j;k++) g[i][j]=max(g[i][j],g[i][k]+g[k+1][j]); } } } printf("%lld",max(DP(1,n),(ll)0)); }