分析:
根据题意我们发现,小a每次排队的时候,会出现两种情况
- 当前排入的人比初始队列中前一个人矮,排到最左边
- 当前排入的人比初始队列中前一个人高,排到最右边
现在给定我们一个理想队列,然后根据这个理想队列推理出有多少种初始队列
根据这个理想队列,其实我们可以发现,固定住一个点之后,初始队列在他后面的人,一定是不断加入到这个点左右两边的。所以固定住这个点之后,可以模拟不断向左向右加人,来获得理想队列。加人实际上就是一个区间左右端点的问题
于是就可以从理想队列出发,运用区间dp的思想,不断枚举元区间,从而获得最后的答案
问题:上一个人是谁?区间左右端点?
根据这个问题,需要记录的状态应该如下
状态:左右端点,上一次插入在哪
状态表示:所有区间[i,j]还原成初始队列,且最后一次插入在 左/右 的集合
初始化:全部初始化为0
边界:根据第一次插入的情况,dp[i][j][1]=0或者dp[i][j][0]=1 (选择一个即可)!!!!,
因为dp[i][i][0/1]都定义的话会重复计算
子集划分/状态计算:
根据最后一次插入的情况,可以划分为如下不重不漏的四个子集
当前插入在左
- 上一次是在右 <a[j] dp[i][j][0]=max(dp[i+1][j][1])
- 上一次是在左 <a[i] dp[i][j][0]=max(dp[i+1][j][0])
当前插入在右
- 上一次是在右 >a[j-1] dp[i][j][1]=max(dp[i][j-1][1])
- 上一次是在左 >a[i] dp[i][j][1]=max(dp[i][j-1][0])
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1005;
const int mod = 19650827;
typedef long long ll;
int a[N];
int b[N];
ll dp[N][N][2];
int main()
{
int n;
cin >> n;
for (int i = 1;i <= n;i++)
cin >> a[i], b[i] = a[i];
for (int len = 1;len <= n;len++)
for (int i = 1;i + len - 1 <= n;i++)
{
int j = i + len - 1;
if (len == 1) dp[i][j][1] = 1;
//else if (len == 2) dp[i][j][0] = dp[i][j][1] = 1;
else
{
ll& x = dp[i][j][0];
ll& y = dp[i][j][1];
if (a[i] < a[j])
x = (x + dp[i + 1][j][1]) % mod;
if (a[i] < a[i + 1])
x = (x + dp[i + 1][j][0]) % mod;
if (a[j] > a[i])
y = (y + dp[i][j - 1][0]) % mod;
if (a[j] > a[j - 1])
y = (y + dp[i][j - 1][1]) % mod;
}
}
ll res = (dp[1][n][0] + dp[1][n][1])%mod;
cout << res;
}