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