题目描述
为了在即将到来的晚会上有更好的演出效果,作为 AAA 合唱队负责人的小 A 需要将合唱队的人根据他们的身高排出一个队形。假定合唱队一共 nn 个人,第 ii 个人的身高为 h_ihi 米(1000 \le h_i \le 20001000≤hi≤2000),并已知任何两个人的身高都不同。假定最终排出的队形是 AA 个人站成一排,为了简化问题,小 A 想出了如下排队的方式:他让所有的人先按任意顺序站成一个初始队形,然后从左到右按以下原则依次将每个人插入最终棑排出的队形中:
-
第一个人直接插入空的当前队形中。
-
对从第二个人开始的每个人,如果他比前面那个人高(hh 较大),那么将他插入当前队形的最右边。如果他比前面那个人矮(hh 较小),那么将他插入当前队形的最左边。
当 nn 个人全部插入当前队形后便获得最终排出的队形。
例如,有 66 个人站成一个初始队形,身高依次为 1850, 1900, 1700, 1650, 1800, 17501850,1900,1700,1650,1800,1750,
那么小 A 会按以下步骤获得最终排出的队形:
-
18501850。
-
1850, 19001850,1900,因为 1900 > 18501900>1850。
-
1700, 1850, 19001700,1850,1900,因为 1700 < 19001700<1900。
-
1650, 1700, 1850, 19001650,1700,1850,1900,因为 1650 < 17001650<1700。
-
1650, 1700, 1850, 1900, 18001650,1700,1850,1900,1800,因为 1800 > 16501800>1650。
-
1750, 1650, 1700, 1850, 1900, 18001750,1650,1700,1850,1900,1800,因为 1750 < 18001750<1800。
因此,最终排出的队形是 1750, 1650, 1700, 1850, 1900, 18001750,1650,1700,1850,1900,1800。
小 A 心中有一个理想队形,他想知道多少种初始队形可以获得理想的队形。
请求出答案对 1965082719650827 取模的值。
输入格式
第一行一个整数 nn。
第二行 nn 个整数,表示小 A 心中的理想队形。
输出格式
输出一行一个整数,表示答案 \bmod 19650827mod19650827 的值。
输入输出样例
输入 #1复制
4
1701 1702 1703 1704
输出 #1复制
8
说明/提示
对于 30\%30% 的数据,n \le 100n≤100。
对于 100\%100% 的数据,n \le 1000n≤1000,1000 \le h_i \le 20001000≤hi≤2000。
思路:这题感觉跟https://blog.youkuaiyun.com/wan1314mum/article/details/111315731差不多的,特点都是每次区间都逐渐增加一个元素变大的。
然而,我还是想了好一会。。。
其实我想对了大半,差一维维护方向了,捂脸233
刚开始想(可跳过,记录心路历程)
f[i][j]:表示构造区间【i,j】的方案数。
f[i][j]的前一个状态有:
1、当前添加的是第i个,又分为2种情况
第i个的前一个是i+1 条件: a[i]<a[i+1] f[i][j]+=f[i+1][j];
第i个的前一个是第j个 条件: a[i]<a[j] f[i][j]+=f[i+1][j];
从这里可以看出,我错了.......两种不一样的情况怎么加上的方案数是一样的呢?!!两种情况的前一个状态方向不同呐,所以还需要再开一维数组记录当前数是从哪边来的。
2、当前添加的是第j个,又分为2种情况,与上面类似
正解:
状态 f[i][j][0]: 区间【i,j】的最后一个数i是从左边来的
f[i][j][1]: 区间【i,j】的最后一个数j是从右边来的。
状态转移:跟上面一样,见代码部分。
答案:dp[1][n][1]+dp[1][n][0] 注意取模
边界值: 因为当只有一个数的时候,只有一种方案,所以 只需要初始化一个方向即可。 f[i][i][0]=1;
#include<bits/stdc++.h>
using namespace std;
const int mod=19650827;
long long a[1005],dp[1005][1005][2];
int main()
{
int n;
cin>>n;
for(int i=1; i<=n; i++){
cin>>a[i];
dp[i][i][1]=1;
}
for(int l=2; l<=n; l++){
for(int i=1; i+l-1<=n; i++){
int j=l+i-1;
//i从左边来,并且上一状态j从右边来
if(a[i]<a[j]){
dp[i][j][0]=(dp[i][j][0]+dp[i+1][j][1])%mod;
}
//i从左边来,并且上一状态i+1从左边来
if(a[i]<a[i+1]){
dp[i][j][0]=(dp[i][j][0]+dp[i+1][j][0])%mod;
}
//j从右边来,并且上一状态j-1从右边来
if(a[j-1]<a[j]){
dp[i][j][1]=(dp[i][j][1]+dp[i][j-1][1])%mod;
}
//j从右边来,并且上一状态i从左边来
if(a[i]<a[j]){
dp[i][j][1]=(dp[i][j][1]+dp[i][j-1][0])%mod;
}
}
}
cout<<(dp[1][n][0]+dp[1][n][1])%mod<<endl;
return 0;
}
总结:再次注意,这种分方向来的,考虑要不要加一维数组维护方向。。