题目大意:给出一个长度为n的数组,这个数组有的数是给出的,有的数是固定的,且范围都在[1,200]之间,要求这个数组中,每一个数字都小于等于 前后两个数字的最大值,求方案数mod p。
思路:一眼看出是个dp,但是不太擅长这个,看了大佬的题解,又加上了一些自己的思考。
由于这个数组每一个元素都是前后相关的,所以应该是个线性dp的东西,既然是线性的,我们就先考虑每一个元素和前面一个元素的关系(没法往后看,因为后面的元素都没有得到),将当前这个数字和前面的数字进行比较,会得到“>”“=”“<”这样三种状态,如果我们用dp[ i ][ x ][ flag ]表示第i个位子填x,和前面元素关系是flag,flag有0,1,2三种状态,分别表示大于,等于,小于。我们可以得到
dp[i+1][ y ][0]=dp[i][ x ][0,1,2] (x<y)
dp[i+1][ y ][1]=dp[i][ y ][0,1,2] (x==y)
dp[i+1][ y ][2]=dp[i][ x ][1,2] (x>y)
而对于第一个位置来说,显然不会小于等于前一个,否则第二个格子就可以随便填了,所以第一个格子只有状态为0,值才等于1.
得到了这个递推式就可以dp了,需要注意的是,对于第一条递推式,由于我要累加所有满足(x<y)的x,很多人可能会枚举x,但事实上我们只需要注意一下x的遍历顺序,把每次的值都累计在一个sum里面就可以了。
但是这道题我们更应该思考的是,为什么要这样dp?
我的理解是,由于每个元素的限制条件其实是当前元素和前后元素的大小关系,也就是说合法性和大小关系有关,所以就对大小关系进行拆解(其实我觉得拆成<=和>就够了)。而我保证了当前格子的情况都是合法的情况,不断递推,递推到最后一个格子时,最后一个格子的绝对合法情况就是答案,即dp[n][x][1]+dp[n][x][2];
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//#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 ",sum); }