解题思路
设置两个二维数组 $f$ 和 $g$,含义如下。
$f[l][r]$ 表示在期望得到的队形中 $l ightarrow r$ 这段区间初始队形排列的方案数,并且最后一个加入进去的是第 $l$ 个人。
$g[l][r]$ 表示在期望得到的队形中 $l ightarrow r$ 这段区间初始队形排列的方案数,并且最后一个加入进去的是第 $r$ 个人。
那么可以看出来 $g[l][r]$ 可以从 $g[l][r-1]$ 跟新出来,$f[l][r]$ 可以从 $f[l+1][r]$ 跟新出来。
大致可以分为下面四种情况:
- 现在决策的这个人插到队伍的最左边,即当前这个人比前一个加入队列的要矮,针对 $f[l][r]$
- 前一个排进去的人是在第 $l+1$ 的位置上,$f[l+1][r] ightarrow a[l]<a[l+1]$
- 前一个排进去的人是在第 $r$ 的位置上,$g[l+1][r] ightarrow a[l]<a[r]$
- 现在决策的这个人插到队伍的最右边,即当前这个人比前一个加入队列的要高,针对 $g[l][r]$
- 前一个排进去的人是在第 $l$ 的位置上,$f[l][r-1] ightarrow a[r]>a[l]$
- 前一个排进去的人是在第 $r-1$ 的位置上,$g[l][r-1] ightarrow a[r]>a[r-1]$
根据上面的关系就可以写出状态转移方程
记得要初始化 $l=r$ 的数组,还有就是枚举区间长度的时候从 $2$ 开始枚举,否则会改变 之前初始化的数组造成答案出错
附上代码
#include <iostream> #include <cstring> #include <cstdio> using namespace std; const int maxn = 1003, HA = 19650827; int n, f[maxn][maxn], g[maxn][maxn], a[maxn]; int main() { scanf("%d", &n); for(int i=1; i<=n; i++) scanf("%d", &a[i]); for(int i=1; i<=n; i++) f[i][i] = 1; static int r; for(int i=2; i<=n; i++) { for(int l=1; l+i-1<=n; l++) { r = l+i-1; f[l][r] = f[l+1][r] * (a[l]<a[l+1]) % HA + g[l+1][r] * (a[l]<a[r]) % HA; f[l][r] = f[l][r] % HA; g[l][r] = f[l][r-1] * (a[r]>a[l]) % HA + g[l][r-1] * (a[r]>a[r-1]) % HA; g[l][r] = g[l][r] % HA; } } printf("%d", (g[1][n]+f[1][n]) % HA); }