题意:
就是给你一个序列,每一个key值对应一个value值,可以把两个连续的key值gcd!=1的对取出来,获取他们的价值,在它们连边的数字因此也变得连续了,问能获取的最大价值
分析:
枚举区间,如果两端不互素且中间部分全取,则dp[i][j]=dp[i+1][j-1]+value[i]+value[j];,判断全取就用前缀和判断,否则就枚举中间点k进行更新。
分析转自:点击打开链接
区间DP还没有写过,感觉套路不深,赶紧去学一学!
代码:
#include <set>
#include <map>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <queue>
#include <stack>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <cassert>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#define PI acos(-1.0)
#define MOD 1000000009
#define INF 0x3f3f3f3f
#define Lowbit(x) (x & (-x))
#define mem(a,x) memset(a,x,sizeof(a))
#define Read() freopen("in.txt", "r", stdin);
#define Write() freopen("out.txt", "w", stdout);
#define bitnum(a) __builtin_popcount(a)
using namespace std;
typedef long long int ll;
inline int in()
{
int res=0;char c;int f=1;
while((c=getchar())<'0' || c>'9')if(c=='-')f=-1;
while(c>='0' && c<='9')res=res*10+c-'0',c=getchar();
return res*f;
}
const int maxn = 305;
ll dp[maxn][maxn];
ll key[maxn];
ll val[maxn];
ll sum[maxn];
int n;
ll gcd(ll a, ll b){
return b == 0 ? a: gcd(b,a%b);
}
void DP(){
mem(dp,0);
for(int len=1;len<=n;len++){ //区间长度
for(int i=1;i+len<=n;i++){
int j = i + len;
for(int k=i;k<j;k++)
dp[i][j] = max(dp[i][j],dp[i][k] + dp[k+1][j]); //枚举中间点K进行更新
if(gcd(key[i] , key[j]) >1){
if(j == i + 1) dp[i][j] = val[i] + val[j];
else if(dp[i+1][j-1] == sum[j-1] - sum[i])
dp[i][j] = max(dp[i][j],dp[i+1][j-1] + val[i] + val[j]);
}
}
}
printf("%lld\n",dp[1][n]);
}
int main(){
int T = in();
while(T--){
mem(sum,0);
scanf("%d",&n);
for(int i=1;i<=n;i++) key[i] = in();
for(int i=1;i<=n;i++) val[i] = in() , sum[i] = sum[i-1] + val[i];
DP();
}
}
用刚学的姿势过了一发。
typedef long long int ll;
inline int in()
{
int res=0;char c;int f=1;
while((c=getchar())<'0' || c>'9')if(c=='-')f=-1;
while(c>='0' && c<='9')res=res*10+c-'0',c=getchar();
return res*f;
}
ll dp[2005][2005] , a[105];
ll key[322];
ll val[322];
ll sum[333];
ll gcd(ll n, ll m){
return m == 0 ? n: gcd(m,n%m);
}
int main(){
int T = in();
while(T--){
int n = in();
for(int i=1;i<=n;i++) key[i] = in();
mem(sum,0);
for(int i=1;i<=n;i++) {
val[i] = in();
sum[i] = val[i] + sum[i-1];
}
mem(dp,0);
for(int i=n-1;i>=1;i--){
for(int j=i+1;j<=n;j++){
for(int k=i+1;k<=j;k++){
dp[i][j] = max(dp[i][j],dp[i][k-1] + dp[k][j]); //必须枚举一遍,找到最大的分割点
}
if(gcd(key[i],key[j]) > 1 ){
if(j == i+1)
dp[i][j] = val[i] + val[j];
else if(dp[i+1][j-1] == sum[j-1] - sum[i]){
dp[i][j] = dp[i+1][j-1] + val[i]+ val[j];
}
}
}
}
printf("%lld\n",dp[1][n]);
}
return 0;
}