P3205 [HNOI2010]合唱队

题目描述

为了在即将到来的晚会上有更好的演出效果,作为 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;
}

总结:再次注意,这种分方向来的,考虑要不要加一维数组维护方向。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值