hdu1133

Problem Description
The
" Harry Potter and the Goblet of Fire " will be on show in the next few days. As a crazy fan of Harry Potter, you will go to the cinema and have the first sight, won’t you ?

Suppose the cinema only has one ticket
- office and the price for per - ticket is 50 dollars. The queue for buying the tickets is consisted of m + n persons (m persons each only has the 50 - dollar bill and n persons each only has the 100 - dollar bill).

Now the problem
for you is to calculate the number of different ways of the queue that the buying process won ' t be stopped from the first person till the last person.
Note: initially the ticket - office has no money.

The buying process will be stopped on the occasion that the ticket
- office has no 50 - dollar bill but the first person of the queue only has the 100 - dollar bill.


Input
The input file contains several test cases. Each test
case is made up of two integer numbers: m and n. It is terminated by m = n = 0 . Otherwise, m, n <= 100 .


Output
For each test
case , first print the test number (counting from 1 ) in one line, then output the number of different ways in another line.


Sample Input
3 0
3 1
3 3
0 0


Sample Output
Test #
1 :
6
Test #
2 :
18
Test #
3 :
180

卡特兰数的应用~~~  ^_^
1133 公式推导如下 :
//MiYu原创, 转帖请注明 : 转载自 ______________白白の屋

( C(m
+ n, n) - C(m + n, m + 1 ) ) * m ! * n ! 化简即 (m + n) ! * (m - n + 1 ) / (m + 1 )

推导过程如下 :

m个人拿50,n个人拿100

1 :    所以如果 n > m,那么排序方法数为 0 这一点很容易想清楚

2 :    现在我们假设 拿50的人用 ‘ 0 ’表示, 拿100的人用 1 表示。

      如果有这么一个序列
0101101001001111 .

      当第K个位置出现1的个数多余0的个数时就是一个不合法序列了

      假设m
= 4 n = 3的一个序列是: 0110100 显然,它不合法, 现在我们把它稍微变化一下:

      把第二个1(这个1前面的都是合法的)后面的所有位0变成1,1变成0

      就得到
0111011 这个序列1的数量多于0的数量, 显然不合法, 但现在的关键不是看这个序列是不是合法的

      关键是:它和我们的不合法序列
0110100 成一一对应的关系

      也就是说任意一个不合法序列(m个0,n个1), 都可以由另外一个序列(n
- 1个0和m + 1个1)得到

      另外我们知道,一个序列要么是合法的,要么是不合法的

      所以,合法序列数量
= 序列总数量 - 不合法序列的总量

      序列总数可以这样计算m
+ n 个位置中, 选择 n 个位置出来填上 1 , 所以是 C(m + n, n)

      不合法序列的数量就是: m
+ n 个位置中, 选择 m + 1 个位置出来填上 1 所以是 C(m + n, m + 1 )

      然后每个人都是不一样的,所以需要全排列 m
! * n !
     
     所以最后的公式为 :  ( C(m+n, n) - C(m+n, m+1) ) * m! * n! 化简即 (m+n)! * (m-n+1) / (m+1)

推广:
      如果原来有p张50元的话,那么不合法的序列的数量应该是:任意一个不合法序列(m个0,n个1),

      都可以由另外一个序列(n
- 1个0和m + 1 + p个1)得到,所以是m + n 个位置中, 选择 m + 1 + p 个位置

      出来填上
1 所以是 C(m + n, m + 1 + p) 接下来的化简就不推了.


代码如下 :
//MiYu原创, 转帖请注明 : 转载自 ______________白白の屋

#include
< iostream >
#include
< string >
using namespace std;
#define MAX 100
#define BASE 10000
void multiply( int a[], int Max, int b)  // 大数乘小数
{
   
int i,array = 0 ;
   
for (i = Max - 1 ; i >= 0 ; i -- )  
    {
        array
+= b * a[i];
        a[i]
= array % BASE;
        array
/= BASE;  
    }
}

void divide( int a[], int Max, int b)  // 大数除小数
{
   
int i,div = 0 ;
   
for (i = 0 ;i < Max; i ++ )  
    {
        div
= div * BASE + a[i];
        a[i]
= div / b;
        div
%= b;
    }
}
int fact[ 205 ][MAX];
void setFact ()
{
     fact[
0 ][MAX - 1 ] = fact[ 1 ][MAX - 1 ] = 1 ;
    
for ( int i = 2 ; i <= 200 ; ++ i )
     {
           memcpy ( fact[i] , fact[i
- 1 ] , MAX * sizeof ( int ) );
           multiply ( fact[i] , MAX , i );
     }
}
void outPut ( int ctl[MAX] )
{
    
int i = 0 ;
    
while ( i < MAX && ctl[i] == 0 )
     {
             i
++ ; // 去前导0
     }
     printf (
" %d " , ctl[i ++ ] );            
    
while ( i < MAX )  
     {
             printf (
" %04d " , ctl[i ++ ] );  
     }
     putchar (
' \n ' );
}
int res[MAX];
int main ()
{
    
int M,N;
    
int ca = 1 ;
     setFact();
    
while ( cin >> M >> N , M + N )
     {
             printf (
" Test #%d:\n " ,ca ++ );
            
if ( N > M )
             {
                  puts (
" 0 " );
                 
continue ;
             }
             memcpy ( res , fact[M
+ N] , MAX * sizeof ( int ) );      阶乘 ( m + n ) !
             multiply ( res, MAX, M
- N + 1 );                               ( m + n ) ! * ( m - n + 1 )
             divide ( res, MAX, M
+ 1 );                                        ( m + n ) ! * ( m - n + 1 / ( m + 1 )        
             outPut ( res );
     }
    
return 0 ;
}

 

 

 

一道排列组合的题 

先把所有的n个拿50的人排好,接下来再把拿100的人插入队列。求可行的概率。

        n个拿50的人有n+1个空位,除了第一个以外,其他的位置都可以,所以概率是n/n+1,在以上的基础下,第2个的的概率是n-1/n(由于是求概率,所以可以把所有的拿50100的人看成是一样的,在这个前提下可以认为第一个拿50的人和第一个拿100的人一起消失了。为什么可以这么认为?由于在求概率,不去除的化会重复计算,具体读者自己思考)。

同理直到最后一个拿100的插入队列,他成功的概率是n-m+1/n-m+2

以上所有都发生,才使最后的队伍满足要求。所以最后成功的概率是n-m+1/n+1。再乘上全部的排列就是答案(m+n)!*(n-m+1)/(n+1)

这样就可以了吗?不是的,注意题目数据范围,还要使用高精度才可以AC

//定义了一个长整数的结构
//2061 长数的乘法运算,运用概率公式计算可能的情况数

//P=(a-b+1)/(a+1)

//N=P*(a+b)!

#include <stdio.h>
#include <string.h>

#define MAX_LENGTH 400              //最长的数字的长度

typedef struct                      //定义了长数的结构
{
        char num[MAX_LENGTH];        //具体的每一位上的数字
        int l;                       //数字的长度
}long_int;

void init_long_int(long_int &a)     //初始化全部为0
{
        memset( &a,0,sizeof(a));
}

long_int int_to_long_int(int x)      //返回x对应的长数,错误就打印错误
{
        long_int a;
        init_long_int(a);
        while ( x != 0)
        {
               a.num[a.l++]=x % 10;
               x /= 10;
        }
        return a;
}

long_int multiply_int(long_int &a,int b)        //乘法函数,返回a*b
{
        int jwei,temp,i,j;                       //进位,临时变量
        long_int c;                              //最终结果存放
        init_long_int (c);
        jwei=0 ; temp=0 ; i=0 ; j=0 ;       //以上变量初始化

        if( b == 0 )
      return c;
        for(i = 0 ; i < a.l ; i ++)
        {
               temp = jwei + b * (int)a.num[i];
               c.num[i] = temp % 10 ;
               jwei = temp / 10 ;
        }
        c.l = i;
        while (jwei > 0)
        {
               c.num[c.l++]=jwei%10;
               jwei/=10;
        }
        return c;
}

void long_int_printf(long_int &x)           //长数打印函数
{
        for (int i=x.l-1;i>=0;i--)
        {
               printf("%c",x.num[i]+48);
        }
        return ;
}

int main()
{
        void init_long_int(long_int &a);          //初始化函数a=0
        long_int int_to_long_int(int x);          //返回x对应的长数,错误就打印错误
        long_int multiply_int(long_int &a,int b);       //乘法函数,返回a*b(b是一般长度的)
        void long_int_printf(long_int &x);              //长数打印函数

        int a,b,i,t=0;
        long_int x;

        while (scanf("%d %d",&a,&b)&&(a!=0||b!=0))
        {
               if (b>a)
               {
                      printf("Test #%d:\n0\n",++t);          //无解输出0
                      continue;
               }
               x=int_to_long_int(1);
               for (i=2;i<=(a+b);i++)                        //介乘计算
               {
                      if (i != a+1) x=multiply_int(x,i);        //避免除法,跳过分母
               }
               if (b!=0)
       x=multiply_int(x,(a-b+1));                //这种情况,分母不该被跳过
               printf("Test #%d:\n",++t);
               long_int_printf(x);
               printf("\n");
        }
        return 0;
}

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值