hdu 4489 The King’s Ups and Downs ( 递推 + 排列组合 )

来源:互联网 发布:淘宝卖家怎么查看访客 编辑:程序博客网 时间:2024/05/21 14:00

The King’s Ups and Downs

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 184 Accepted Submission(s): 116


Problem Description
The king has guards of all different heights. Rather than line them up in increasing or decreasing height order, he wants to line them up so each guard is either shorter than the guards next to him or taller than the guards next to him (so the heights go up and down along the line). For example, seven guards of heights 160, 162, 164, 166, 168, 170 and 172 cm. could be arranged as:


or perhaps:


The king wants to know how many guards he needs so he can have a different up and down order at each changing of the guard for rest of his reign. To be able to do this, he needs to know for a given number of guards, n, how many different up and down orders there are:

For example, if there are four guards: 1, 2, 3,4 can be arrange as:

1324, 2143, 3142, 2314, 3412, 4231, 4132, 2413, 3241, 1423

For this problem, you will write a program that takes as input a positive integer n, the number of guards and returns the number of up and down orders for n guards of differing heights.

Input
The first line of input contains a single integer P, (1 <= P <= 1000), which is the number of data sets that follow. Each data set consists of single line of input containing two integers. The first integer, D is the data set number. The second integer, n (1 <= n <= 20), is the number of guards of differing heights.

Output
For each data set there is one line of output. It contains the data set number (D) followed by a single space, followed by the number of up and down orders for the n guards.

Sample Input
41 12 33 44 20

Sample Output
1 12 43 104 740742376475050

Source
Greater New York 2012

Recommend
liuyiding

题意:
已知有N个士兵,求将他们排成高低高。。或者 低高低。。。
这种高低交错的一队有多少种排法。

分析:
关键词:递推
已知i-1个人高低交错的排法,现在多出一个人i,要求i加入队伍后仍然满足
高低交错。
dp[i]表示i个人高低交错的排法数

第i个人怎么入队呢?
分类——1、他可以直接站在队伍的两边
               2、他可以插入到队伍中去
第一种情况:站在队伍两边可以直接入队,因此方案数和i-1个人时相等。
第二种情况:插入队,怎么插?——要满足插入的两边,左边以波峰结束,右边以波峰开始。
                       或者左边以波谷结束,右边以波谷开始。
                       
                       这样,我们从i-1个人中选j个人站在他左边,另外i-1-j个人站在他右边。
                        从i-1个人中选j个人的方案数用组合数表示就是C(i-1,j)
                        由于j个人的高低交错排列数和i-1-j个的排列数已知或者已经在前面推出,
                        故i个人的排列方案数为:
                                 dp[i]=(dp[j]>>1)*(dp[i-1-j]>>1)*C(i-1,j)。
                       当人数比较少时是可以直接看出排列数的:
                        dp[1]=1,dp[2]=2,dp[3]=4;
                       因此可以从n=4开始递推,这些作为递推的初始条件
                        注意:(1) 当j=1或者i-1-j=1时要特判。因为dp[1]=1.
                                   (2) 要用__int64
AC代码:
#include<cstdio>#include<iostream>#include<cstring>using namespace std;__int64 dp[21];__int64 pow(int n)  //求阶乘{    __int64 sum=1;    for(int i=2;i<=n;i++)        sum*=i;    return sum;}__int64 c(int n,int m)   //算组合数{    return pow(n)/pow(m)/pow(n-m);}   //应用组合数公式C(m,n)=n!/(m!*(n-m)!)int main(){    int T,cas,t,i,j;    memset(dp,0,sizeof(dp));    dp[1]=1,dp[2]=2,dp[3]=4;    for(i=4;i<=20;i++)    {        dp[i]+=dp[i-1];   //最高的在两边*C(i,0)        for(j=1;j<i-1;j++)        {            if(j==1||i-j-1==1)     //如果左边只有1人或者右边只有1人                dp[i]+=(dp[j]*dp[i-j-1]>>1)*c(i-1,j);            else                dp[i]+=(dp[j]>>1)*(dp[i-j-1]>>1)*c(i-1,j);          }  //高低高 和 低高低 的排法各占总排法的1/2    }      //两边只能都是高低高 或者 都是低高低 才能保证插入一个人符合站法    scanf("%d",&T);    while(T--)    {        scanf("%d %d",&cas,&t);        printf("%d %I64d\n",cas,dp[t]);    }    return 0;}

9008101

2013-08-19 20:03:12

Accepted

4489

0MS

228K

791 B

C++


还有一种做法,就是把左边和右边区分开,开个二维数组dp[21][2],dp[][0]表示左边的排法数,
dp[][1]表示右边的排列方案数。

求组合数也直接用二维数组c[i][j]表示i个中取j个。

AC代码:
#include<cstdio>#include<iostream>#include<cstring>using namespace std;__int64 dp[21][2],sum;__int64 c[21][21];int main(){    int T,cas,x,i,j;    for(i=1;i<21;i++)    {        c[i][0]=1;    //c[i][j]表示i个中选j个的方案数        c[i][i]=1;        for(j=1;j<i;j++)            c[i][j]=c[i-1][j-1]+c[i-1][j];  //公式,结合杨辉三角理解    }    dp[1][1]=dp[1][0]=dp[0][0]=dp[0][1]=1;      for(i=2;i<21;i++)    {        sum=0;        for(j=0;j<i;j++)            sum+=dp[j][0]*dp[i-1-j][1]*c[i-1][j];        dp[i][0]=sum/2;      //后面递推要用        dp[i][1]=sum/2;    }    scanf("%d",&T);    while(T--)    {        scanf("%d%d",&cas,&x);        printf("%d %I64d\n",cas,x>1?dp[x][1]*2:1);    }    return 0;}


9008284

2013-08-19 20:19:06

Accepted

4489

0MS

232K

688 B

C++