n<=1e5
dp[i][j][k]表示当前第i个数字为j,第i-1个数字与第i个之间大小关系为k的方案数(a[i-1]<a[i],=,>)
转移时使用前缀和和后缀和加速
【状态转移】:
因为情况已经分成三种情况了,小于,等于,大于。
然后根据题目意思,就是不能出现一种情况,a[i-1] < a[i] > a[i+1]
就是说,当我们转移:"大于"时,前一个状态不能是”小于“。
【小结】:
道理我都懂,但我就是写不出来。一头雾水,但是我看了看题解,我就醍醐灌顶了。。。
【代码】(里面有详细的解释)
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int mod = 998244353; 5 const int N = 1e5+5; 6 const int M = 205; 7 ll f[N][M][3],tmp; 8 int a[N],n; 9 10 /* 11 f[i][j][k] 12 第i个位置上,的数值为j,与前一个数的关系为k. 13 14 k = 0 a[i-1] < a[i] / 15 k = 1 a[i-1] == a[i] - 16 k = 2 a[i-1] > a[i] 17 18 */ 19 20 21 int main() 22 { 23 scanf("%d",&n); 24 for(int i=1;i<=n;i++) scanf("%d",&a[i]); 25 26 /* 27 由于题意让我们a[1] <= a[2] 28 我们初始化时,在1的位置上 29 必须让f[1][i][0],以防止 a[1] > a[2]的情况 30 */ 31 32 for(int i=1;i<=200;i++){ 33 if( a[1] == -1 || i == a[1] ) f[1][i][0] = 1 ; 34 else f[1][i][0] = 0 ; 35 } 36 //[2,n]上进行状态转移 37 for(int i=2;i<=n;i++){ 38 39 //枚举当前位置的值,‘=‘的情况 , k = 1 ‘-‘ 40 for(int j=1;j<=200;j++){ 41 if( a[i] == -1 || a[i] == j ) 42 f[i][j][1] = (f[i-1][j][0]+f[i-1][j][1]+f[i-1][j][2])%mod; 43 else 44 f[i][j][1] = 0; 45 } 46 47 //枚举当前位置的值,‘<‘的情况 , k = 0 ‘/‘ 48 //∵a[i-1] < a[i] 49 //前一个位置可能有多种情况,所以当前位置应该是记录前缀和 50 tmp = 0 ; 51 for(int j=1;j<=200;j++){ 52 if( a[i] == -1 || a[i] == j ) 53 f[i][j][0] = tmp ; 54 else f[i][j][0] = 0 ; 55 tmp = (tmp + f[i-1][j][0] + f[i-1][j][1] + f[i-1][j][2] ) % mod ; 56 } 57 58 //枚举当前位置的值,‘>‘的情况, k = 2 ‘\‘ 59 //∵a[i-1] > a[i] 60 //前一个位置可能有多种情况,所以当前位置应该是记录后缀和 61 tmp = 0 ; 62 for(int j=200;j>=1;j--){ 63 if( a[i] == -1 || a[i] == j ) 64 f[i][j][2] = tmp ; 65 else f[i][j][2] = 0; 66 tmp = ( tmp + f[i-1][j][1] + f[i-1][j][2] ) %mod ; 67 } 68 } 69 ll ans = 0 ; 70 71 /* 72 题目要求: 73 ∵a[n-1] >= a[n] 74 ∴从两种状态进行转移,k=1. 75 */ 76 for(int i=1;i<=200;i++){ 77 ans = ( ans + f[n][i][1] + f[n][i][2] ) % mod ; 78 } 79 80 printf("%lld\n",ans); 81 return 0 ; 82 }
【计数dp】Array Without Local Maximums
原文:https://www.cnblogs.com/Osea/p/11319570.html