The King’s Ups and Downs
题意:给你N个人的身高,他们身高各不相同,问排列是高低高低高低、或低高低高低高的方案数是多少?
分析
此问题的阶段性很容易看出来,就是先求出N = 1
的排列数,再求出N=2
的排列书,然后再求N=3
,再求N = i
的排列数时,可能要用到N = 1,N = 2,N = 3...N = i-1
的排列数。那如何去找前后阶段的关联性,试想一下,假如上一下阶段N = i-1
的排列方案已经求了出来,现在要将第i
个人插入到此排列中,假如插入到第j个位置,那么需要知道j位置之前j-1
个人以高低结尾的方案数,还需要知道j
位置之后i-1-j
个人以低高开始的方案数,很幸运的是在此阶段之前小于等于i-1
的排列数就已经求出来了,他包括高低,低高两种情况排列
所以:j-1
个人以高低结尾的方案数 = j-1
排列的方案数/2
(前面阶段已经求出)i-1-j
个人以低高开始的方案数 = i-1-j
排列的方案数/2
(前面阶段已经求出)
所以状态表示:dp[i][0]
:表示i个人以高低结尾的方案数dp[i][1]
:表示i个人以低高开始的方案数sum[i]
:i个人的排列数C[i][j]
:从i个人选j个人
状态转移:sum[i] = C[i-1][j]*dp[j][0]*dp[i-1-j][1]
插入的位置,前面有j个人,i-1是上一阶段的人数
AC代码
#include <iostream> #include <algorithm> using namespace std; const int maxn = 1e6+10; typedef long long ll; int T,id,N; ll C[25][25]; ll sum[25]; ll dp[25][2];//dp[i][0]:i个人最后以高低结尾的方案数,dp[i][1]:i个人以低高开始的方案数 void initC(){ //初始化组合数C C[1][1] = 1; for(int i = 0;i<=20;i++) C[i][0] = 1; for(int i = 1;i<=20;i++){ for(int j = 1;j<=i;j++){ C[i][j] = C[i-1][j]+C[i-1][j-1]; } } } void solve(){ dp[0][0] = 1,dp[0][1] = 1;dp[1][0] = 1;dp[1][1] = 1; sum[1] = 1; for(int i = 2;i<=20;i++){ for(int j = 0;j<=i-1;j++){ sum[i] += C[i-1][j]*dp[j][0]*dp[i-1-j][1]; } dp[i][0] = dp[i][1] = sum[i]/2; //因为sum[i]包括高低、低高两种情况 } } int main(){ initC(); solve(); cin>>T; while(T--){ scanf("%d%d",&id,&N); printf("%d %lld\n",id,sum[N]); } return 0; }