DP做题记录

一.CF1389B Array Walk

题意
在这里插入图片描述
思路
可以枚举向右走1~k步的情况,每向右走一步,可以选择向左来回走0~z次,然后取总分的最大值,由于z不大于5,因此复杂度为 O ( n ) O(n) O(n)

代码

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<cstring>
#include<string>
#define ll long long
using namespace std;
typedef pair<int,int> pii;
const int N=2e5+10;
const double esp=1e-5;
const ll mod=1e9+7;

int t,n,k,z,a[N],sum[N];
int main()
{
    cin>>t;
    while(t--){
        cin>>n>>k>>z;
        memset(sum,0,sizeof(sum));
        for(int i=1;i<=n;i++){
            cin>>a[i];
            sum[i]=sum[i-1]+a[i];
        }

        int ans=0,tmp;
        for(int i=1;i<=n&&k>0;i++,k--){
            tmp=sum[i];
            for(int j=0;j<=z&&2*j<=k;j++){
                int r=k-2*j;//来回走j次之后还剩余的步数
                tmp=max(tmp,(a[i]+a[i+1])*j+sum[i+r]);
            }
            ans=max(tmp,ans);
        }
        cout<<ans<<endl;
    }
    return 0;
}

二.CF255C Almost Arithmetical Progression

题意
先给出一个整数 n n n ,再给出一个有 n n n 个元素的序列 b b b
现在要你求序列 b b b 中最长的子序列,满足隔位的两个数相等,问这个最长的子序列的长度是多少。
思路
在这个题中, b b b数组的范围很大,所以应该要离散化.
然后,设 f i , a j f_{i,a_j} fi,aj表示在前 i i i个数中,另一个数是 a j a_{j} aj(离散化后)时满足条件的最大子序列长度,所以 f i , a j f_{i,a_j} fi,aj应满足: f i , a j = m a x ( f i , a j , f j , a i + 1 ) f_{i,a_j}=max(f_{i,a_j},f_{j,a_i}+1) fi,aj=max(fi,aj,fj,ai+1)
时间复杂度为 O ( n 2 ) O(n^2) O(n2)

代码

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<string>
#include<map>
#include<cmath>
#include<bitset>
#define ll long long
using namespace std;
const int N=1e6+10;
const double esp=1e-5;
const ll mod=1e9+7;

//f[i][a[j]]:表示在前i个数中,另一个数是a[j]时符合条件的最大长度
int n,a[N],f[4005][4005],cnt;
int ans;
map<int,int> mp;
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        if(!mp[a[i]])//用map离散化
            mp[a[i]]=++cnt;
    }
    if(n<2)
        ans=n;
    else{
        for(int i=2;i<=n;i++){
            for(int j=1;j<i;j++){
                ans=max(ans,f[i][mp[a[j]]]=max(f[i][mp[a[j]]],f[j][mp[a[i]]]+1));
            }
        }
        ans++;
    }
    cout<<ans<<endl;
    return 0;
}
/*
11
12 8 12 8 6 8 6 12 8 8 6
8
2 2 2 3 2 3 2 4
3
2 2 2
5
3 3 2 3 3
*/

三.AcWing4378. 选取数对

题意

在这里插入图片描述
思路
可以设 f [ i ] [ j ] = f[i][j]= f[i][j]=为以 a [ i ] a[i] a[i]结尾,选取 j j j个区间时的总和最大值.
先用一个s数组保存前缀和,然后枚举1~k个区间,在1~k中,枚举j*m到n来求每一个 f [ i ] [ j ] f[i][j] f[i][j],最后取 f [ i ] [ k ] f[i][k] f[i][k]的最大值即可

代码

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<string>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
typedef pair<int,int> pii;
const int N=5005;
const double esp=1e-5;
const ll mod=1e9+7;

int n,m,k;
//f[i][j]:以a[i]结尾,选取j个区间时的总和最大值
ll a[N],f[N][N],s[N];
int main()
{
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
        s[i]+=a[i]+s[i-1];
    }

    for(int i=1;i<=k;i++){
        ll maxf=0;
        for(int j=i*m;j<=n;j++){
            maxf=max(maxf,f[j-m][i-1]+s[j]-s[j-m]);
            f[j][i]=maxf;
        }
    }
    ll ans=0;
    for(int i=1;i<=n;i++)
        ans=max(ans,f[i][k]);
    printf("%lld\n",ans);
    return 0;
}
/*
5 2 1
1 2 3 4 5
*/

四. NC234864 造桥

在这里插入图片描述
思路
定义dp数组:dp[i][j]为在前i个字符串中,以j字符结尾的最大字符串长度,则状态方程为
d p [ i ] [ t a i l ] = m a x ( d p [ i − 1 ] [ t a i l ] , d p [ i − 1 ] [ h e a d ] + l e n ) dp[i][tail]=max(dp[i-1][tail],dp[i-1][head]+len) dp[i][tail]=max(dp[i1][tail],dp[i1][head]+len)
其中tail为第i个字符串的最后一个字符,head为第i个字符串的第一个字符.
代码

#include<iostream>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<vector>
#include<cstring>
#include<string>
#include<map>
#include<cmath>
#define ll long long
#define SE_IT set<node>::iterator
#define inf 0x3f3f3f3f
#define bug(a) cout<<"* "<<a<<endl;
#define bugg(a,b) cout<<"** "<<a<<" "<<b<<endl;
#define buggg(a,b,c) cout<<"*** "<<a<<" "<<b<<" "<<c<<endl;
using namespace std;
typedef pair<int,int> pii;
const int N=2e5+10;
const double esp=1e-5;
const ll mod=1e9+7;

//dp[i][j]:在前i个字符串中,以j字符结尾的最大字符串长度
int t,n,dp[N][30];
string s[N];
int main()
{
     cin>>t;
     while(t--){
        cin>>n;
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=n;i++){
            cin>>s[i];
        }
        int ans=0;
        for(int i=1;i<=n;i++){
            int len=s[i].size();
            int head=s[i][0]-'a',tail=s[i][len-1]-'a';
            for(int j=0;j<26;j++){
                if(j==tail){
                    dp[i][tail]=max(dp[i-1][tail],dp[i-1][head]+len);
                    // bug(dp[i][tail])
                }
                else
                    dp[i][j]=dp[i-1][j];
            }
            // bug(dp[i][tail])
            ans=max(ans,dp[i][tail]);
        }
        cout<<ans<<endl;
     }
    return 0;
}

五.CF788A Functions again

题意
在这里插入图片描述

思路
此题为dp求最大连续子序列和,根据题意,子序和有两种情况,所以应该求两次取最大的一次即可
代码

#include<iostream>
#include<algorithm>
#include<queue>
#include<set>
#include<unordered_map>
#include<stack>
#include<cstdio>
#include<cctype>
#include<vector>
#include<cstring>
#include<string>
#include<map>
#define ll long long
#define endl "\n"
#define inf 0x3f3f3f3f
using namespace std;
typedef pair<int,int> pii;
const int N=2e5+10;
const double esp=1e-5;
const ll mod=1e9+7;

int t,n;
ll a[N],ans=-inf;
//dp[i]:到当前为止,以a[i]为最后一个数的最大子序和
ll dp[N];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    for(int i=1;i<n;i++)
        a[i]=abs(a[i+1]-a[i]);
    for(int i=1;i<n;i++){
        if(i&1){
            dp[i]=max(dp[i-1]+a[i],a[i]);
        }
        else
            dp[i]=max(dp[i-1]-a[i],-a[i]);
        //bug(dp[i])
        ans=max(dp[i],ans);
    }
    memset(dp,0,sizeof(dp));
    for(int i=2;i<n;i++){
        if(i&1){
            dp[i]=max(dp[i-1]-a[i],-a[i]);
        }
        else
            dp[i]=max(dp[i-1]+a[i],a[i]);
        //bug(dp[i])
        ans=max(dp[i],ans);
    }
    cout<<ans<<endl;
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值