题目描述
为了在即将到来的晚会上有更好的演出效果,作为 AAA 合唱队负责人的小 A 需要将合唱队的人根据他们的身高排出一个队形。假定合唱队一共 �n 个人,第 �i 个人的身高为 ℎ�hi 米(1000≤ℎ�≤20001000≤hi≤2000),并已知任何两个人的身高都不同。假定最终排出的队形是 �A 个人站成一排,为了简化问题,小 A 想出了如下排队的方式:他让所有的人先按任意顺序站成一个初始队形,然后从左到右按以下原则依次将每个人插入最终棑排出的队形中:
-
第一个人直接插入空的当前队形中。
-
对从第二个人开始的每个人,如果他比前面那个人高(ℎh 较大),那么将他插入当前队形的最右边。如果他比前面那个人矮(ℎh 较小),那么将他插入当前队形的最左边。
当 �n 个人全部插入当前队形后便获得最终排出的队形。
例如,有 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 取模的值。
输入格式
第一行一个整数 �n。
第二行 �n 个整数,表示小 A 心中的理想队形。
输出格式
输出一行一个整数,表示答案 mod 19650827mod19650827 的值。
输入输出样例
输入 #1复制
4 1701 1702 1703 1704
输出 #1复制
8
说明/提示
对于 30%30% 的数据,�≤100n≤100。
对于 100%100% 的数据,�≤1000n≤1000,1000≤ℎ�≤20001000≤hi≤2000。
思路:
使用区间DP求解,对于队列1701 1702 1703 1704,对于1704后放的种数应该是:队列 1701 1702 1703中 1701 后放的情况(条件:1704 > 1701)+ 队列 1701 1702 1703中 1703 后放的情况(条件:1704 > 1703)都是有判断条件的,而判断条件是队列的一前一后(或是一左一右)两个元素的大小。因此,一个元素进行排队时有两种可能,该元素的前一个元素到底是大于前前一个元素而放在右边,还是小于前前一个元素而放在左边的。通俗来讲就是该元素到底是大于最左边的元素而插入到最右边的,还是大于最右边前一个元素而插入到最右边的。
设队列为i、i+1、i+2、...、j-1、j(区间队列,因此是从i开始,而不是从1开始,逐渐迭代到插入的第一个元素),因此状态转移方程为:
小于插入左边:f[j][i] = f[i + 1][j] (arr[i] < arr[j]) + f[j][i + 1] (arr[i] < arr[i + 1])
大于插入右边:f[i][j] = f[i][j - 1] (arr[j] > arr[j - 1]) + f[j - 1][i] (arr[j] > arr[i])
比如:1 2 3这个理想队列,那可以排列成该队列的第一位只能是1或3(要么插入左边,要么插入右边),则可能的队列为:
以1为第一位
1 2 3 (2为第二位)
1 3 2 (3为第二位)
以3为第一位
3 1 2 (1为第二位)
3 2 1 (2为第二位)
因为代码实现其实很简单,因此建议看到这可以去试试自己写出代码,实在写不出再看下面的代码。
全AC代码:
import java.util.Scanner;
public class Main {
public static void main(String[] args){
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int[] arr = new int[1000];
int[][] dp = new int[1000][1000];
for(int i = 0; i < n; i++){
arr[i] = scanner.nextInt();
dp[i][i] = 1;
}
for(int i = 1;i < n;i++){
for(int j = 0;i + j < n;j++){
int k = i + j;
if(arr[j] < arr[j + 1]){
dp[k][j] += dp[k][j + 1];
}
if(arr[j] < arr[k] && j != k - 1){
dp[k][j] += dp[j + 1][k];
}
if(arr[k] > arr[k - 1]){
dp[j][k] += dp[j][k - 1];
}
if(arr[k] > arr[j] && j != k - 1){
dp[j][k] += dp[k - 1][j];
}
dp[j][k] %= 19650827;
dp[k][j] %= 19650827;
}
}
System.out.println((dp[0][n - 1] + dp[n - 1][0]) % 19650827);
}
如果 arr[j] < arr[j + 1],说明当前子数组的起始元素小于下一个元素,则在已知的 dp[k][j + 1] 的基础上增加一个符合条件的子数组。因此, dp[k][j] 增加 dp[k][j + 1]。
如果 arr[j] < arr[k] 且 j != k - 1,说明当前子数组的起始元素小于末尾元素,则在已知的 dp[j + 1][k] 的基础上增加一个符合条件的子数组。因此, dp[k][j] 增加 dp[j + 1][k]。
如果 arr[k] > arr[k - 1],说明当前子数组的末尾元素大于前一个元素,则在已知的 dp[j][k - 1] 的基础上增加一个符合条件的子数组。因此,将dp[j][k] 增加 dp[j][k - 1]。
如果 arr[k] > arr[j] 且 j != k - 1,说明当前子数组的末尾元素大于起始元素,则在已知的 dp[k - 1][j] 的基础上增加一个符合条件的子数组。因此,将dp[j][k] 增加 dp[k - 1][j]。
ps:不知道为啥洛谷上的测试点竟然没有输入为1的,毕竟一个人是直接入队,哪里有参照物向左插入还是向右插入。