HDU5900QSC and Master (区间DP)

题意:

就是给你一个序列,每一个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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值