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