The King’s Ups and Downs

今天又开始做DP,今天这个题是一个组合递推DP,一开始在思考加入一个新的人后,会对前面的数列有什么影响,但是卡在了,这个新加入的人的高度,就直接看了题解。

才发现状态的划分是按照从小到大的顺序来进行的,这样也很符合状态的设计,因为递推性,当有n - 1个不同身高的人已经排好时,再加入一个更高的,其实也复合逻辑,并且之所以这样设计状态对于问题来说也是便于处理和正确的。

接着就是加入这个新的人后,之前的状态到底与新状态有什么关系。

先来考虑新的N加入的位置,因为条件要求的是,对于任意一个人来说,和它相邻的人必须比他高或者矮,这样的话因为新加入的这个人是这N个人中最高的,那这样的话,这个新加入的人只能插入在一个他的前面是 高低 的组合后面,同理考虑加入的这个新数后面的情况的话,就要是 低高 的情况,因为要保证后面的也满足条件.这个我陷入了一个思维的误区,就是片面的以为这个新的状态是通过它前一个状态,也就是 i - 1时的状态得来,但今天想明白了之后,明白递推不一定是仅仅由它前一个的状态得来,而是由 “前面” 出现过的 状态 得来.更多的是去发现,寻找这个当前状态与前面的那些状态有关,进而 “递推”.

言归正传,接着上面的讲,那样的话,这个问题就转化成了,当前状态是由前面的以高低结尾的状态 和 开头是以 低高的状态得来. 具体的思考一下.因为新加入的这个数改变了当前的最大值,所以,对于这个状态的设计可任选一些数 来 获得 “高低”关系.所以的话.
状态可以这样设计,
dp[i][0] 是 当序列长度为 i 时,以低高 结尾的方法数.
dp[i][1] 是 当序列长度为 i 时,以高低 结尾的方法数.

设 j 是新插入的位置,sum[i] 为 长度为 i 时的方法总数
sum[i] = dp[j - 1][0] * dp[i - j ][1] * c(j - 1,i - 1)
要乘组合数的原因是因为高低状态只与高度的大小有关,因为前面记录的状态都是 “较小数量”的状态,所以应该先从当前数量选取应选的数量.
然后枚举插入的位置就可以了,
最后还有一点就是,dp之间的递推关系,
这个的话,我是通过样例的找到的关系,就是dp[i][0] = dp[i][1] = sum[i] / 2;
其实也可以通过对称性来考虑.这个目前还没明白……….就先不写了.

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int MAXN = 25;
typedef long long LL;
LL sum[MAXN];
LL dp[MAXN][MAXN];
LL c(int m,int n)
{
    LL a = 1;
    LL b = 1;
    for(int i = n;i >= n - m + 1;--i)
    {
        a *= i;
    }
    for(int i = 1;i <= m;++i)
    {
        b *= i;
    }
    return a / b;
}
void f1()
{
    memset(sum,0,sizeof(sum));
    dp[0][0] = dp[0][1] = 1;
    dp[1][0] = dp[1][1] = 1;
    sum[1] = 1;
    for(int i = 2;i <= 21;++i)
    {
        for(int j = 1;j <= i;++j)
        {
            sum[i] += dp[j - 1][0] * dp[i - j][1] * c(j - 1,i - 1);
        }
        dp[i][0] = dp[i][1] = (sum[i] / 2);
    }
    return ;
}
int main()
{
    f1();
    //cout << f(2,3) << endl;
    int T;
    cin >> T;
    while(T--)
    {
        int k,m;
        cin >> k >> m;
        cout << k << ' ' << sum[m] << endl;
    }
    return 0;
}

通过这个题 了解到了状态之间的转化,和状态的设计.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值