题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=4489
题目意思:
给一个n,求n个高矮不同的人排成一排使得高、矮依次排列的种数。
解题思路:
组合dp.
对于n个人,设其高度分别为1,2,3,,,,,n.
对于第n个人,假设前面的n-1个人已经放好了。第n个人有n个位置可放,对于任一位置j,显然第n个人的身高n大于前n-1个人的任何人的身高。所以第n个人左边的j-1个人的排列
中,必须满足最后一个人一定是通过身高下降得到的,右边的n-j个人中,最开始的那个人一定通过升高得到后面一个人的,当然前面j-1个人可选C(n-1,j-1)。
所以设状态dp[i][0]表示有i个人,并且第一个人通过上升得到第二个人的总的排列种数,i个人身高肯定不一样,故只考虑个数。
dp[i][1]表示有i个人,并且最后一个人是通过下降得到的。
很显然在人数相同的情况下,由对称性得 dp[i][0]=dp[i][1]=sum[i]/2 sum[i]为i个人总的满足要求的排列数。
对于第n个人放到位置j ,有C(n-1,j-1)*dp[j-1][0]*dp[n-j][1]种情况。
代码:
#include <iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<cctype>
#include<map>
#include<set>
#include<queue>
#include<vector>
using namespace std;
const double eps = 1e-5;
const double PI = acos(-1.0);
typedef __int64 ll;
ll dp[30][2];
ll sum[30];
ll Cal(int a,int b) //求出Cab 其实可以用递推来算的,Cab=C(a-1)(b-1)+C(a-1)b
{
if(b==0)
return 1;
ll res=1;
for(int i=0;i<b;i++)
{
res*=(a-i);
}
for(int i=1;i<=b;i++)
res/=i;
return res;
}
int main()
{
dp[0][0]=dp[0][1]=1;
dp[1][0]=dp[1][1]=1;
sum[1]=1;
for(int i=2;i<=20;i++)
{
sum[i]=0;
for(int j=1;j<=i;j++)
{
sum[i]+=(dp[j-1][0]*dp[i-j][1]*Cal(i-1,j-1));
}
dp[i][0]=dp[i][1]=sum[i]/2;//由对称性
//printf("i:%d %I64d \n",i,sum[i]);
}
// printf("%d %I64d\n",20,sum[20]);
int t;
int d,k;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&d,&k);
printf("%d %I64d\n",d,sum[k]);
}
return 0;
}