【Codeforces Round #518 (Div. 2) [Thanks, Mail.Ru!] D. Array Without Local Maximums】DP+滚动数组优化

本文介绍了一种使用动态规划(DP)解决特定数组性质问题的方法,针对一种特殊的数组,其元素满足特定的不等式关系。通过定义DP状态和转移方程,文章详细解释了如何计算可能的数组状态数,特别关注于处理模糊输入和优化DP过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


D. Array Without Local Maximums

题意

本来有一个n个数字的数组,数字大小从1到200本来有一个n个数字的数组,数字大小从1到200n1200
现在有些数字看不清了,但是只记得原数组有一种性质现在有些数字看不清了,但是只记得原数组有一种性质
a[2]>=a[1]a[2]>=a[1]a[2]>=a[1]
a[n−1]>=a[n]a[n-1]>=a[n]a[n1]>=a[n]
a[i]&lt;=max(a[i−1],a[i+1])   2&lt;=i&lt;=n−1a[i]&lt;=max(a[i-1],a[i+1]) \ \ \ 2&lt;=i&lt;=n-1a[i]<=max(a[i1],a[i+1])   2<=i<=n1

做法
对于这个数据范围,很显然就能想到dp对于这个数据范围,很显然就能想到dpdp
定义DP状态为dp[i][j][k]定义DP状态为dp[i][j][k]DPdp[i][j][k]
dp[i][j][0]表示第i个数为j而且小于前一个数的状态数dp[i][j][0] 表示第i个数为j而且小于前一个数的状态数dp[i][j][0]ij
dp[i][j][1]表示第i个数为j而且等于前一个数的状态数dp[i][j][1] 表示第i个数为j而且等于前一个数的状态数dp[i][j][1]ij
dp[i][j][2]表示第i个数为j而且大于前一个数的状态数dp[i][j][2] 表示第i个数为j而且大于前一个数的状态数dp[i][j][2]ij
转移方程也很显然,只不过对于第二个位置的dp数组要特殊处理转移方程也很显然,只不过对于第二个位置的dp数组要特殊处理dp
而且需要滚动数组对dp进行前缀和优化而且需要滚动数组对dp进行前缀和优化dp

代码

#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define dbg(x) cout<<#x<<" = "<<x<<endl
typedef long long ll;
const ll Mod = 998244353 ;
const int maxn = 1e5+10;
ll dp[maxn][201][3];
ll a[maxn];
ll sum1[maxn],sum2[maxn];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
    if(a[1]==-1)
    {
        if(a[2]==-1)
        {
            for(int j=1;j<=200;j++)
            {
                dp[2][j][1]=1;
                dp[2][j][2]=j-1;
            }
        }
        else
        {
            dp[2][a[2]][1]=1;
            dp[2][a[2]][2]=a[2]-1;
        }
    }
    else
    {
        if(a[2]==-1)
        {
            for(int j=a[1]+1;j<=200;j++)
            {
                dp[2][j][2]=1;
            }
            dp[2][a[1]][1]=1;
        }
        else
        {
            if(a[1]==a[2]) dp[2][a[2]][1]=1;
            else if(a[1]<a[2])  dp[2][a[2]][2]=1;
        }
    }
    for(int j=1;j<=200;j++) sum1[j]=(sum1[j-1]+dp[2][j][0]+dp[2][j][1]+dp[2][j][2])%Mod;
    for(int j=200;j>=1;j--) sum2[j]=(sum2[j+1]+dp[2][j][0]+dp[2][j][1])%Mod;
    for(int i=3;i<=n;i++)
    {
        if(a[i]!=-1)
        {
            dp[i][a[i]][0]=sum2[a[i]+1];
            dp[i][a[i]][1]=(dp[i-1][a[i]][0]+dp[i-1][a[i]][1]+dp[i-1][a[i]][2])%Mod;
            dp[i][a[i]][2]=sum1[a[i]-1];
        }
        else
        {
            for(int j=1;j<=200;j++)
            {
                dp[i][j][0]=(dp[i][j][0]+sum2[j+1])%Mod;
                dp[i][j][1]=(dp[i-1][j][0]+dp[i-1][j][1]+dp[i-1][j][2])%Mod;
                dp[i][j][2]=(dp[i][j][2]+sum1[j-1])%Mod;
            }
        }
        for(int j=1;j<=200;j++) sum1[j]=(sum1[j-1]+dp[i][j][0]+dp[i][j][1]+dp[i][j][2])%Mod;
        for(int j=200;j>=1;j--) sum2[j]=(sum2[j+1]+dp[i][j][0]+dp[i][j][1])%Mod;
    }
    ll ans=0;
    for(int i=1;i<=200;i++)
    {
        ans=(ans+dp[n][i][0]+dp[n][i][1])%Mod;
    }
    printf("%lld\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值