您的位置:首页 > 理论基础 > 计算机网络

hdu 5900 QSC and Master(区间dp 沈阳网络赛)

2016-09-19 19:29 537 查看
题目: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

;
int a
;
ll b
,sum
;
bool ok

,match

;
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: