BZOJ 1996 [HNOI2010] chorus 合唱队 区间DP

题目描述 Description
为了在即将到来的晚会上有更好的演出效果,作为AAA合唱队负责人的小A需要将合唱队的人根据他们的身高排出一个队形。假定合唱队一共N个人,第i个人的身高为Hi米(1000<=Hi<=2000),并已知任何两个人的身高都不同。假定最终排出的队形是A 个人站成一排,为了简化问题,小A想出了如下排队的方式:他让所有的人先按任意顺序站成一个初始队形,然后从左到右按以下原则依次将每个人插入最终棑排出的队形中:

  • 第一个人直接插入空的当前队形中。

  • 对从第二个人开始的每个人,如果他比前面那个人高(H较大),那么将他插入当前队形的最右边。如果他比前面那个人矮(H较小),那么将他插入当前队形的最左边。

当N个人全部插入当前队形后便获得最终排出的队形。

例如,有6个人站成一个初始队形,身高依次为1850、1900、1700、1650、1800和1750,

那么小A会按以下步骤获得最终排出的队形:

  • 1850

  • 1850 , 1900 因为 1900 > 1850

  • 1700, 1850, 1900 因为 1700 < 1900

  • 1650 . 1700, 1850, 1900 因为 1650 < 1700

  • 1650 , 1700, 1850, 1900, 1800 因为 1800 > 1650

  • 1750, 1650, 1700,1850, 1900, 1800 因为 1750 < 1800

因此,最终排出的队形是 1750,1650,1700,1850, 1900,1800

小A心中有一个理想队形,他想知道多少种初始队形可以获得理想的队形

输入描述 Input Description
第一行一个正整数N,表示总人数
第二行使用空格隔开的N个正整数,从左到右表示小A心中的理想队形:H1,H2,H3…Hn

输出描述 Output Description
仅包含一个数,表示从多少种初始队形出发能通过上述排队的方式获得小A心中的理想队形.因为满足条件的初始队形数可能很大,所以规定只要输出满足条件的初始队形数mod19650827的值

样例输入 Sample Input
4
1701 1702 1703 1704

样例输出 Sample Output
8

数据范围及提示 Data Size & Hint
对于30%的数据, 1<=N<=100 1 <= N <= 100
对于100%的数据, 1<=N<=1000,1000<=Hi<=2000 1 <= N <= 1000 , 1000 <= H i <= 2000

Solution

很容易就能看出这是一个区间DP吧(雾
通过手推发现每个序列 [l,r] [ l , r ] 只有可能从 [l,r1] [ l , r − 1 ] [l+1,r] [ l + 1 , r ] 转移
同时如何转移还要看之前序列的朝向
所以用
f[l][r][0] f [ l ] [ r ] [ 0 ] 表示 [l,r] [ l , r ] 区间内最后朝向左的序列个数
f[l][r][1] f [ l ] [ r ] [ 1 ] 表示 [l,r] [ l , r ] 区间内最后朝向右的序列个数

显然的通过题目条件转移
if(a[l]<a[r])f[l][r][0]+=f[l+1][r][1]; i f ( a [ l ] < a [ r ] ) f [ l ] [ r ] [ 0 ] + = f [ l + 1 ] [ r ] [ 1 ] ;
if(a[l]<a[l+1])f[l][r][0]+=f[l+1][r][0]; i f ( a [ l ] < a [ l + 1 ] ) f [ l ] [ r ] [ 0 ] + = f [ l + 1 ] [ r ] [ 0 ] ;
if(a[r]>a[r1])f[l][r][1]+=f[l][r1][1]; i f ( a [ r ] > a [ r − 1 ] ) f [ l ] [ r ] [ 1 ] + = f [ l ] [ r − 1 ] [ 1 ] ;
if(a[r]>a[l])f[l][r][1]+=f[l][r1][0]; i f ( a [ r ] > a [ l ] ) f [ l ] [ r ] [ 1 ] + = f [ l ] [ r − 1 ] [ 0 ] ;

代码奇短,好懂,自己YY吧QwQ

代码如下:

#include <bits/stdc++.h>
using namespace std;
#define r l+lenth-1
const int N = 1005;
const int mod = 19650827;
int n;
int a[N],b[N];
int f[N][N][2];
int read() {
    int ans=0,flag=1;
    char ch=getchar();
    while((ch<'0' || ch>'9') && ch!='-') ch=getchar();
    if(ch=='-') { flag=-1;ch=getchar(); }
    while(ch>='0' && ch<='9') { ans=ans*10+ch-'0';ch=getchar(); }
    return ans*flag;
}
int main() {
    n=read();
    for(int i=1;i<=n;++i) a[i]=read(),b[i]=a[i];
    sort(b+1,b+n+1);unique(b+1,b+n+1);
    for(int i=1;i<=n;++i) a[i]=lower_bound(b+1,b+n+1,a[i])-b;
    for(int lenth=1;lenth<=n;++lenth) {
        for(int l=1;l+lenth-1<=n;++l) {
            if(lenth==1) {f[l][r][1]=1;continue;}
            if(a[l]<a[r]) f[l][r][0]+=f[l+1][r][1];
            if(a[l]<a[l+1]) f[l][r][0]+=f[l+1][r][0];
            if(a[r]>a[r-1]) f[l][r][1]+=f[l][r-1][1];
            if(a[r]>a[l]) f[l][r][1]+=f[l][r-1][0];
            f[l][r][0]%=mod;f[l][r][1]%=mod;
        }
    }
    printf("%d\n",(f[1][n][0]+f[1][n][1])%mod);
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
题目描述 有一个 $n$ 个点的棋盘,每个点上有一个数字 $a_i$,你需要从 $(1,1)$ 走到 $(n,n)$,每次只能往右或往下走,每个格子只能经过一次,路径上的数字和为 $S$。定义一个点 $(x,y)$ 的权值为 $a_x+a_y$,求所有满足条件的路径中,所有点的权值和的最小值。 输入格式 第一行一个整数 $n$。 接下来 $n$ 行,每行 $n$ 个整数,表示棋盘上每个点的数字。 输出格式 输出一个整数,表示所有满足条件的路径中,所有点的权值和的最小值。 数据范围 $1\leq n\leq 300$ 输入样例 3 1 2 3 4 5 6 7 8 9 输出样例 25 算法1 (树形dp) $O(n^3)$ 我们可以先将所有点的权值求出来,然后将其看作是一个有权值的图,问题就转化为了在这个图中求从 $(1,1)$ 到 $(n,n)$ 的所有路径中,所有点的权值和的最小值。 我们可以使用树形dp来解决这个问题,具体来说,我们可以将这个图看作是一棵树,每个点的父节点是它的前驱或者后继,然后我们从根节点开始,依次向下遍历,对于每个节点,我们可以考虑它的两个儿子,如果它的两个儿子都被遍历过了,那么我们就可以计算出从它的左儿子到它的右儿子的路径中,所有点的权值和的最小值,然后再将这个值加上当前节点的权值,就可以得到从根节点到当前节点的路径中,所有点的权值和的最小值。 时间复杂度 树形dp的时间复杂度是 $O(n^3)$。 C++ 代码 算法2 (动态规划) $O(n^3)$ 我们可以使用动态规划来解决这个问题,具体来说,我们可以定义 $f(i,j,s)$ 表示从 $(1,1)$ 到 $(i,j)$ 的所有路径中,所有点的权值和为 $s$ 的最小值,那么我们就可以得到如下的状态转移方程: $$ f(i,j,s)=\min\{f(i-1,j,s-a_{i,j}),f(i,j-1,s-a_{i,j})\} $$ 其中 $a_{i,j}$ 表示点 $(i,j)$ 的权值。 时间复杂度 动态规划的时间复杂度是 $O(n^3)$。 C++ 代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值