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