Queue

Queue

       可以说是排列组合,可以说是动态规划,还可以说是递推。其实,可以直接递推打表,后面直接输出就还了,相当于也是以空间换时间。

       有n个人(高度不一)排成一列,从队列的最前看只能看到p个人,从队列的最后面看只能看到r个人(有的人可能被遮住了)。问排成这种队列的排法有多少种。

       对于有n个人的队列,那是n-1个人的队列里面加了一个人(假设最身高最矮)。这个人可能加在中间,那么他有n-2个位置可以站,最前最后两个位置不能站,就有【n-1个人,前看p个人,后看r个人】*(n-2)中排法;要是这个人排在队列的最前面,则有【n-1个人,前看p-1个人(加1后就是p个人),后看r个人】;同理,要是他站在队伍最前面,则有(n-1个人,前看p个人,后看r-1个人)重排法。

       递推方程:dp[i][j][k]=dp[i-1][j][k]*(i-2)+dp[i-1][j][k-1]+dp[i-1][j-1][k];

        dp[i][j][k]表示共有i个人排成一列,从前看能看到j个人,从后看能看到k个人的排法有dp[i][j][k]种。

       (1)若i==j+k-1并且j,k有一个等于1,dp[i][j[][k]=1,这时候是整个队伍的人从前到后按身高严格升序或严格降序排列的。或是最高的在中间,两边式严格像中间递增的。这些情况,都只有一种排法。

        (2)否则,若i=1并且j=1,则dp[i][j][k]=0,因为最高那个人不可能即在队头,又在队尾。注,n=1的这种情况在上面就已经处理了,不会走到这一步

        (3)否则,直接用公式算就好了。

      在输出的时候还要注意,要是j+k-1>i(题目没说输入的p,r之和不大于n+1),那么dp[i][j][k]=0。这种队列不可能排的出来,至于为什么,读者朋友自己思考,实在想不通的,请留言。


include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int main()
{
    int n,p,r;
    long long dp[14][14][14];
    int i,j,k;
    memset(dp,0,sizeof(dp));
    dp[0][0][0]=1;
    for(i=1; i<14; i++)
    {
        for(j=1; j<=i; j++)
        {
            for(k=1; k<=i-j+1; k++)
            {
                if((i==j+k-1)&&(j==1||k==1))   //递增(或递减)有序排列
                    dp[i][j][k]=1;
                else if(j==1&&k==1)dp[i][j][k]=0;   //不可能排列
                else
                    dp[i][j][k]=dp[i-1][j][k]*(i-2)+dp[i-1][j][k-1]+dp[i-1][j-1][k];   //随机有可能排列
            }
        }
    }
    int t;
    cin>>t;
    while(t--)
    {
        cin>>n>>p>>r;
        if(p+r>n+1)cout<<"0"<<endl;  //注意细节啊,鄙人就在这里WA了一次
        else cout<<dp[n][p][r]<<endl;
    }
    return 0;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值