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; }