Array Without Local Maximums(永远不可能学会的动态规划之计数DP)

                            Array Without Local Maximums

Ivan unexpectedly saw a present from one of his previous birthdays. It is array of nn numbers from 1 to 200. Array is old and some numbers are hard to read. Ivan remembers that for all elements at least one of its neighbours ls not less than it, more formally:

a1≤a2,

an≤an−1 and

ai≤max(ai−1,ai+1) for all ii from 2 to n−1.

Ivan does not remember the array and asks to find the number of ways to restore it. Restored elements also should be integers from 1 to 200. Since the number of ways can be big, print it modulo 998244353.

Input

First line of input contains one integer nn (2≤n≤105) — size of the array.

Second line of input contains nn integers aiai — elements of array. Either ai=−1 or 1≤ai≤200. ai=−1 means that i-th element can't be read.

Output

Print number of ways to restore the array modulo 998244353998244353.

Input

3
1 -1 2

Output

1

Input

2
-1 -1
200

In the first example, only possible value of a2 is 2.

In the second example, a1=a2 so there are 200 different values because all restored elements should be integers between 1 and 200.

题意:

有一个长度为n的序列,满足对于所有的a[x],与它相邻的两个元素a[x-1]和a[x+1]中至少有一个大于等于它,其中a[1]和a[n]当然只有一个相邻元素(可以默认i<1 || i>n 的数组值为0 即(a[0]==0 || a[n+1]==0)),现在这个序列中的有些数字被破坏了(标记为-1),问有多少种合法恢复方案(每个数字∈[1,200])

题解:

对于一可行序列,只考虑最后三位,共有如下排列方式

排列顺序

此图来自别人博客

那么对于这9种情况只有 这一种情况是是不符合的排除这一种即可

a_{i-1}a_{i}有三种关系,<,=,>。这里的dp[i][j][k]代表第i个位置为为j且与前面的a_{i-1}的关系为k的方案数

那么状态转移方程为

1. dp[i][j][0]=\sum_{k=1}^{j}(dp[i−1][k][0]+dp[i−1][k][1]+dp[i−1][k][2])

因为a_{i-1}<a_{i} 时,那么a_{i-1}可以和a_{i-2}满足任何条件

2. dp[i][j][1]=dp[i−1][j][0]+dp[i−1][j][1]+dp[i−1][j][2]

这里与1的不同之处就是a_{i-1}=a_{i}

3. dp[i][j][2]=\sum_{k=j}^{200}​(dp[i−1][k][2]+dp[i−1][k][1])

因为a_{i-1}>a_{i},所以必须要a_{i-1}<=a_{i-2}

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int maxn=1e5+5;
const int mod=998244353;
int a[maxn];
ll dp[maxn][205][3];
// 0: a[i-1] < a[i]   1: a[i-1] = a[i]   2: a[i-1] < a[i]
int main()
{
    int n;
    memset(dp,0,sizeof(dp));
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    //可以理解为<1的位置a[i](即a[0])一定为0,不存在其他可能只可能小于a[1] 并且 a1≤a2
    if(a[1]!=-1)dp[1][a[1]][0]=1;
    else for(int j=1;j<=200;j++)dp[1][j][0]=1;

    ll sum;
    for(int i=2;i<=n;i++)
    {
        sum=0;
        for(int j=1;j<=200;j++)
        {
            if(a[i]==-1 || a[i]==j)dp[i][j][0]=sum;
            sum=(sum+dp[i-1][j][0]+dp[i-1][j][1]+dp[i-1][j][2])%mod;
        }
        for(int j=1;j<=200;j++)
            if(a[i]==-1 || a[i]==j)dp[i][j][1]=(dp[i-1][j][0]+dp[i-1][j][1]+dp[i-1][j][2])%mod;
        sum=0;
        for(int j=200;j>=1;j--)
        {
            if(a[i]==-1 || a[i]==j)dp[i][j][2]=sum;
            sum=(sum+dp[i-1][j][1]+dp[i-1][j][2])%mod;
        }

    }

    ll ans=0;
    for(int j=1;j<=200;j++)ans=(ans+dp[n][j][1]+dp[n][j][2])%mod;//可以理解为>n的位置a[i] (即a[n+1])一定为0,不存在其他可能只可能小于a[n] an≤an−1

    printf("%lld\n",ans);


}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值