codeforces #462 D - A Twisty Movement - dp

本文介绍了两种不同的动态规划(DP)算法实现方式,一种是通过状态转移方程寻找特定子序列的最大长度,另一种则是通过减少状态维度来优化计算过程。文中提供了完整的C++代码示例,帮助读者更好地理解算法细节。

终于我也开始做dp题了。
链接


做法1:
s=[0,1,2,1,2] s = [ 0 , 1 , 2 , 1 , 2 ] , i i 指数组a的下标, type t y p e 指数组 s s 的下标,均从1开始。

状态转移方程为:

dp[type][i]=max{dp[type1][i],dp[type][i1]+(a[i]=?s[type])}
,表示区间 [1,i] [ 1 , i ] 中形如 1,,2,,1,,1 1 , ⋯ , 2 , ⋯ , 1 , ⋯ , 1 的子序列的最大长度。

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int maxn = 2e3+7;
int a[maxn];
int dp[5][maxn];
int s[] = {0,1,2,1,2};
int main(){
    ios::sync_with_stdio(0);
    int n;cin >> n;
    for(int i = 1;i<=n;i++){
        cin >> a[i];
    }
    for(int i = 1;i<=n;i++){
        for(int type = 1;type<=4;type++){
            dp[type][i] = max(dp[type-1][i],dp[type][i-1]+(a[i] == s[type]));
        }
    }
    cout << dp[4][n] << endl;
}

做法2:

状态转移方程:

dp[i][j][2]=dp[i][j1][2]+(a[j]=?2) d p [ i ] [ j ] [ 2 ] = d p [ i ] [ j − 1 ] [ 2 ] + ( a [ j ] = ? 2 )
,表示区间 [i,j] [ i , j ] 中以2结尾的 不增子序列(形如 [2,2,1,1] [ 2 , 2 , 1 , 1 ] )的长度。

dp[i][j][1]=max{dp[i][j1][1],dp[i][j1][2]}+(a[j]=?1) d p [ i ] [ j ] [ 1 ] = max { d p [ i ] [ j − 1 ] [ 1 ] , d p [ i ] [ j − 1 ] [ 2 ] } + ( a [ j ] = ? 1 )
,表示区间 [i,j] [ i , j ] 中以1结尾的 不增子序列(形如 [1,1,1,1] [ 1 , 1 , 1 , 1 ] )的长度。

sum1[i] s u m 1 [ i ] 为前缀1的数量。 sum2[i] s u m 2 [ i ] 为后缀2的数量。

可以用方程的特性,去掉 dp[] d p [ ] 的两个维度。

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int maxn = 2e3+7;
int a[maxn];
int dp[3];
int sum1[maxn],sum2[maxn];
int main(){
    ios::sync_with_stdio(0);
    int n;cin >> n;
    for(int i = 1;i<=n;i++){
        cin >> a[i];
    }
    for(int i = 1;i<=n;i++){
        sum1[i] += sum1[i-1] + (a[i] == 1);
    }
    for(int i = n;i>=1;i--){
        sum2[i] += sum2[i+1] + (a[i] == 2);
    }
    int ans = -1e9;
    for(int i = 1;i<=n;i++){
        memset(dp,0,sizeof(dp));
        for(int j = i;j<=n;j++){
            dp[1] = max(dp[1],dp[2]) + (a[j] == 1);
            dp[2] = dp[2] + (a[j] == 2);
            ans = max(ans,sum1[i-1]+sum2[j+1]+dp[1]);
            ans = max(ans,sum1[i-1]+sum2[j+1]+dp[2]);
        }
    }
    cout << ans << endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值