Description
为了在即将到来的晚会上有更好的演出效果,作为AAA合唱队负责人的小A需要将合唱队的人根据他们的身高排出一个队形。假定合唱队一共有N个人,第i个人的身高为Hi毫米(1000<=Hi<=2000),并且已知任何两个人的身高都不同。假定最终排出的队形是N个人站成一排,为了简化问题,小A想出了如下排队的方式:他让所有的人先按任意顺序站成一个初始队形,然后从左到右按以下原则依次将每个人插入最终排出的队形中:
- 第一个人直接插入空的当前队形中。
- 对从第二个人开始的每个人,
- - 如果他比前面那个人高(H较大),那么将他插入当前队形的最右边。
- - 如果他比前面那个人矮(H较小),那么将他插入当前队形的最左边。
当N个人全部插入当前队形后便获得最终排出的队形。
例如,有6个人站成一个初始队形,身高依次为1850,1900,1700,1650,1800和1750,那么小A会按以下步骤获得最终出的队形:
- 1850
- 1850,1900
- 1700,1850,1900
- 1650,1700,1850,1900
- 1650,1700,1850,1900,,1800
- 1750,1650,1700,1850,1900,1800
因此,最终排出的队形是1750,1650,1700,1850,1900,1800
小A心中有一个理想队形,他想知道从多少种初始队形出发能通过上述排队的方式获得他心中的理想队形作为最终排出的队形?
Input
第一行是一个正整数N,表示总人数。
输入文件第二行是用空格隔开的N个正整数,从左到右表示小A心中的理想队形:H1, H2, „, HN。
输入的数据保证1000≤Hi≤2000且没有相同的H值.
其中30%的数据满足1≤N≤100,100%的数据满足1≤N≤1000。
Output
仅包含一个数,表示从多少种初始队形出发能通过上述排队的方式获得输入文件中指定的小A心中的理想队形。
因为满足条件的初始队形数可能很大,所以规定只要输出满足条件的初始队形数mod 19650827的值。
Sample Input
输入样例1:
4
1701 1702 1703 1704
输入样例2:
4
1704 1703 1702 1701
Sample Output
输出样例1:
8
输出样例2:
0
Hint
样例解释:
8种初始队形分别为
(1701, 1702, 1703, 1704)
(1704, 1703, 1702, 1701)
(1702, 1701, 1703, 1704)
(1703, 1702, 1701, 1704)
(1702, 1703, 1701, 1704)
(1702, 1703, 1704, 1701)
(1703, 1704, 1702, 1701)
(1703, 1702, 1704, 1701)
【分析】
我果然还是太弱了 Orz... 拿到这道题后居然想了很久才联想到DP...
分析题目可以知道,初始队形中的连续一段一定对应目标队形中的连续一段。
我们这样定义状态数组:
f[i][j]表示目标队形中[i,j]这一段最左边(i)是刚放上去的数的方案数
g[i][j]表示目标队形中[i,j]这一段最右边(j)是刚放上去的数的方案数
那么我们可以得到状态转移方程:
f[i][j]=f[i+1][j]*(h[i]<h[i+1])+g[i+1][j]*(h[i]<h[j])
g[i][j]=f[i][j-1]*(h[j]>h[i])+g[i][j-1]*(h[j]>h[j-1])
再回过头来看这个状态转移方程,如果题目不要求满足条件才可以放在最左边或者最右边,那么就是个单纯的递推:
f[i][j]=f[i+1][j]+g[i+1][j] g[i][j]=f[i][j-1]+g[i][j-1]
而题目加上了对高度的限制,所以我们要判断高度来选择是否继承那些方案,这就是决策。
边界:
f[i][i]=g[i][i]=1 f[i-1][j]=g[i-1][j]=(h[i-1]<h[i])
【代码】
/*
ID:Ciocio
LANG:C++
DATE:2013-12-07
TASK:Chorus
*/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
#define MAXN 1005
#define MODER 19650827
int N;
int h[MAXN];
int f[MAXN][MAXN],g[MAXN][MAXN];
void _init()
{
scanf("%d",&N);
for(int i=1;i<=N;i++)
scanf("%d",&h[i]);
}
void _solve()
{
for(int i=1;i<=N;i++)
{
f[i][i]=g[i][i]=1;
if(i!=1) f[i-1][i]=g[i-1][i]=(h[i-1]<h[i]);
}
for(int k=3;k<=N;k++)
for(int p=1;p+k-1<=N;p++)
{
int i=p,j=p+k-1;
f[i][j]=f[i+1][j]*(h[i]<h[i+1])+g[i+1][j]*(h[i]<h[j]);
g[i][j]=f[i][j-1]*(h[j]>h[i])+g[i][j-1]*(h[j]>h[j-1]);
f[i][j]%=MODER;
g[i][j]%=MODER;
}
cout<<(f[1][N]+g[1][N])%MODER<<endl;
}
int main()
{
_init();
_solve();
return 0;
}