题目:http://acm.hdu.edu.cn/showproblem.php?pid=5900
题意:
给出n对数keyi,vali表示当前这对数的键值和权值,可以操作将连续的两个数合并,如果满足gcd(a[i],a[i+1])>1,得到的价值是两个数的权值和,每次合并两个数之后,这两个数就会消失,然后旁边的数会接上
分析:
1:处理出任意区间内的所有数是否可以合并,对于当前的[l,r]区间,如果区间[l+1,r-1]可以合并,且gcd(a[l]+a[r])>1的话,则整个区间[l,r]可以合并。
2:区间dp,对于当前的l,r区间,如果要拿走l,那么枚举和l可以匹配的点i,并且【l,i】整个区间可以合并,那么就可以拿走l,也可以不选择拿走l。
代码:
#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
const int N=333;
typedef long long ll;
ll dp[N][N];
int a[N];
ll b[N],sum[N];
bool ok[N][N],match[N][N];
int gcd(int a,int b) {
return b==0?a:gcd(b,a%b);
}
bool judge(int l,int r) {
if(l>r)return 1;
return ok[l][r];
}
void init(int n) {
memset(match,0,sizeof(match));
memset(ok,0,sizeof(ok));
for(int i=1; i<=n; i++) {
for(int j=i+1; j<=n; j++)
match[i][j]=gcd(a[i],a[j])>1?1:0;
}
for(int l=1; l<n; l++) {
for(int i=1; i+l<=n; i++) {
int j=i+l;
ok[i][j]=0;
for(int k=i+1; k<=j; k++)
if(match[i][k]&&judge(i+1,k-1)&&judge(k+1,j))ok[i][j]=1;
}
}
}
ll dfs(int l,int r) {
if(l>=r)return 0;
if(dp[l][r]>=0LL)return dp[l][r];
ll& ans=dp[l][r];
ans=0LL;
for(int i=l+1; i<=r; i++) {
if(ok[l][i])ans=max(ans,dfs(i+1,r)+sum[i]-sum[l-1]);
}
ans=max(ans,dfs(l+1,r));
return ans;
}
int main() {
// freopen("f.txt","r",stdin);
int T,n;
scanf("%d",&T);
while(T--) {
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]);
sum[i]=sum[i-1]+b[i];
}
init(n);
memset(dp,-1,sizeof(dp));
printf("%lld\n",dfs(1,n));
}
return 0;
}
本文介绍了一道关于区间动态规划的题目,通过处理键值对并利用区间DP算法解决合并问题。文章详细解释了如何判断区间内元素是否可合并及求解最大价值的方法。
572

被折叠的 条评论
为什么被折叠?



