《The King’s Ups and Downs》超详细题解!(dp+组合数)

作为一个dp蒟蒻,这题也卡了我很久,一开始看大佬们的题解愣是没看懂。
首先这里我们用的思想是dp的思想。
那么应该怎么dp?我们要用前面的人的状态来计算。题目给我们n个数.
并且这个n个数是从1~n的,那么我们可以从小到大开始摆人.
首先摆1,确定1的方案数,然后2,确定2的方案数,然后3,4…
那么我们该怎么确实这个方案数呢?这里就开始用到dp了。
对于我们目前摆到的这个人x,因为我们是从小到大摆的,
那么我们摆到这个x时,前面的x-1个人肯定已经是摆好了的.
那么我们就可以开始摆第x个人的位置了。
对于第x个人他可以摆第一个人前面(写代码时我们把它称为0号位置)。
第一个人后面(即1号位置),2号位置…一共有n-1号位置可以摆.
那么摆进的这个位置它需要满足一定的条件。
1:他的前面的人是下降得到,这样摆进x才能满足条件(因为x肯定比前面的人数字大所以肯定和前面的人组成上升)
2:他后面的人是上升的开始,和1同理这样才能满足条件。

也就是说x摆入的这个位置一定要是符合题意能够组成降升降的.

那么我们可以设置一个dp[i][2].
dp[i][0]表示第i个人以降结尾的方案数.
dp[i][1]表示第i个人以升开始的方案数.
然后由于对称性,我们可以知道dp[i][0] = dp[i][1].
那么我们就可以得到dp的式子 dp[i][0]*dp[x-i-1][1]*C(x-1,i)
然后我们来看一下这个式子。现在我们把x插入第i号位置,那他前面应该有i个人,
后面应该有x-i-1个人.
那么根据刚才的结论这个位置的方案数就是:
前面结尾降的dp[i][0]*后面开头升的dp[i][1]。
前面这i个人我们可以从剩下的x-1个人中任意选i个人来组合
所以要乘以组合的方案数C(x-1,i).
我觉得这种思路比较好的理解方法还是要从插入位子来思考!!!

#include<iostream>
#include<stdio.h>
#include<queue>
#include<algorithm>
#include<math.h>
#include<stack>
#include<map>
#include<limits.h>
#include<vector>
#include<string.h>
#include<string>
using namespace std;
typedef long long LL;
typedef pair<double,int> pii;
const int N = 2005;
const int M = 10007;
#define pi acos(-1)
#define INF 1e9
#define INM INT_MIN
#define pb(a)  push_back(a)
#define mk(a,b) make_pair(a,b)
#define dbg(x) cout << "now this num is " << x << endl;
#define sd(ax) scanf("%d",&ax)
#define sld(ax) scanf("%lld",&ax)
#define sdd(ax,bx) scanf("%d %d",&ax,&bx)
#define sddd(ax,bx,cx) scanf("%d %d %d",&ax,&bx,&cx)
#define pr(a) printf("%d\n",a)
#define plr(a) printf("%lld\n",a)
LL dp[25][2];//注意longlong
LL C(int n,int m)
{
    if(m == 0 || n == m) return 1;
    return C(n-1,m)+C(n-1,m-1);
}
void init()//预处理防止超时
{
    dp[1][0] = 1;dp[1][1] = 1;//第一个位置初始化
    dp[0][0] = 1;dp[0][1] = 1;//第0个位置初始化,第0个位置就是第一个人前面的位置
    for(int k=2;k<=20;++k)//放第k个人
    {
        LL ans = 0;//存第k个人的总方案数
        for(int i=0;i<k;++i)//放置在第i个位子
        {
            int j = k-1-i;//后面的人
            ans += dp[i][0]*dp[j][1]*C(k-1,i);
        }
        dp[k][0] = dp[k][1] = ans/2;
    }
}
int main()
{
    int t;
    init();
    sd(t);
    while(t--)
    {
        int d,n;
        sdd(d,n);
        LL re = dp[n][0]+dp[n][1];
        if(n == 1) re = 1;//1个人的时候比较特殊
        printf("%d %lld\n",d,re);
    }
    system("pause");
    return 0;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值