codeforces 1068d Array Without Local Maximums dp

题目传送门

  题目大意:给出一个长度为n的数组,这个数组有的数是给出的,有的数是固定的,且范围都在[1,200]之间,要求这个数组中,每一个数字都小于等于 前后两个数字的最大值,求方案数mod p。

  思路:一眼看出是个dp,但是不太擅长这个,看了大佬的题解,又加上了一些自己的思考。

  由于这个数组每一个元素都是前后相关的,所以应该是个线性dp的东西,既然是线性的,我们就先考虑每一个元素和前面一个元素的关系(没法往后看,因为后面的元素都没有得到),将当前这个数字和前面的数字进行比较,会得到“>”“=”“<”这样三种状态,如果我们用dp[ i ][ x ][ flag ]表示第i个位子填x,和前面元素关系是flag,flag有0,1,2三种状态,分别表示大于,等于,小于。我们可以得到

  dp[i+1]][0]=dp[i]][0,1,2]   (x<y)

  dp[i+1]][1]=dp[i]][0,1,2]   (x==y)

  dp[i+1]][2]=dp[i]][1,2]      (x>y)

  而对于第一个位置来说,显然不会小于等于前一个,否则第二个格子就可以随便填了,所以第一个格子只有状态为0,值才等于1.

  得到了这个递推式就可以dp了,需要注意的是,对于第一条递推式,由于我要累加所有满足(x<y)的x,很多人可能会枚举x,但事实上我们只需要注意一下x的遍历顺序,把每次的值都累计在一个sum里面就可以了。

  但是这道题我们更应该思考的是,为什么要这样dp?

  我的理解是,由于每个元素的限制条件其实是当前元素和前后元素的大小关系,也就是说合法性和大小关系有关,所以就对大小关系进行拆解(其实我觉得拆成<=和>就够了)。而我保证了当前格子的情况都是合法的情况,不断递推,递推到最后一个格子时,最后一个格子的绝对合法情况就是答案,即dp[n][x][1]+dp[n][x][2];

//#pragma comment(linker,"/STACK:102400000,102400000")
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<vector>
#include<map>
#include<set>
#include<cstring>
#include<cmath>
#include<queue>
#include<stack>
#include<stdlib.h>
//#include<unordered_map>
#define lson l,mid,rt<<1
#define rson mid+1,r,(rt<<1)|1
#define CLR(a,b) memset(a,b,sizeof(a))
#define mkp(a,b) make_pair(a,b)
typedef long long ll;
using namespace std;
inline int read(){
    int x=0,f=1;
    char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;}
const int maxn=1e5+10;
ll p=998244353;
ll dp[maxn][210][3];
int a[maxn],n;
int main(){
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        a[i]=read();
    }
    for(int x=1;x<=200;x++)
    {
        if(a[1]!=-1&&a[1]!=x)dp[1][x][0]=dp[1][x][1]=dp[1][x][2]=0;
        else dp[1][x][0]=1,dp[1][x][1]=dp[1][x][2]=0;
    }
    ll sum;
    for(int i=2;i<=n;i++)
    {
        sum=0;
        for(int x=1;x<=200;x++)
        {
            if(a[i]!=-1&&a[i]!=x)dp[i][x][0]=0;
            else dp[i][x][0]=sum;
            sum=(sum+dp[i-1][x][0]+dp[i-1][x][1]+dp[i-1][x][2])%p;
        }
        for(int x=1;x<=200;x++)
        {
            if(a[i]!=-1&&a[i]!=x)dp[i][x][1]=0;
            else dp[i][x][1]=(dp[i-1][x][0]+dp[i-1][x][1]+dp[i-1][x][2])%p;
        }
        sum=0;
        for(int x=200;x>0;x--)
        {
            if(a[i]!=-1&&a[i]!=x)dp[i][x][2]=0;
            else dp[i][x][2]=sum;
            sum=(sum+dp[i-1][x][1]+dp[i-1][x][2])%p;
        }
    }
    sum=0;
    for(int i=1;i<=200;i++){
        sum=(sum+dp[n][i][1]+dp[n][i][2])%p;
    }
    printf("%lld\n",sum);
}
View Code

 

posted @ 2018-11-08 22:11  光芒万丈小太阳  阅读(418)  评论(0编辑  收藏  举报