【JAVA大数&&卡特兰数】Buy the Ticket HDU - 1133

题目链接

Buy the Ticket HDU - 1133

题目

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

题意

m+n个人排队买票,m个人手中只有50元,n个人手中只有100元。门票50元,初始售票点没有钱找零。求有多少种排队方式使得每个人都能买到票。即没有出现找不开钱的情况。

分析

最早遇到买票找零问题是在《编程之美》这本书上。书中介绍了两种解法:递推和计数原理。
简化问题:
假设拿100元和拿50元的人都是n个。
假设不考虑人与人之间的差别。

递推法:
我们可以将100元看作左括号,50元看作右括号。问题就变成了合法括号问题。
设F(2n)表示n个左括号和n个右括号能组成的合法括号序列的种数。
第一个括号一定是左括号。设第k个括号为右括号,那么k一定为奇数,即k=2i+1。因为只有这样第2个到第k-1个括号(共k-1个,为偶数)才是合法括号序列。
故F(2n)=F(2i)*F(2n-2i-2),0<=i<=n-1.
上述递推公式得到的数叫做卡特兰数。

计数原理:
合法序列数=总序列数-非法序列数。
总序列数有C(2n,n)种。
对于非法序列:
设从第k个人开始无法买票,我们将前k-1个人手中的100变成50,50变成100,那么100的数目变成n+1,50的数目变成n-1。这样非法序列总是就等于C(2n,n+1)。
答案=C(2n,n)-C(2n,n+1)。可以验证,与递推得到的答案是一致的。

回到原问题:
有m个人有50元,有n个人有100元。
根据计数原理的解法,合法序列总数sum有C(n+m,n)-C(n+m,n+1)。
然后人与人之间有差别,sum再乘以n!* m!即可。

AC代码

import java.math.BigInteger;
import java.util.Scanner;

public class Main{
    public static void main(String[] args) {
        Scanner cin=new Scanner(System.in);
        int m,n,c,kase=0;
        BigInteger ans;
        while(cin.hasNext()) {
            ++kase;
            m=cin.nextInt();
            n=cin.nextInt();
            if(n==0 && m==0) break;
            if(m<n) ans=BigInteger.ZERO;
            else {
                ans=BigInteger.ONE;
                ans=(C(n+m,n)).subtract(C(n+m,m+1));
                if(ans.equals(BigInteger.valueOf(0))) ans=BigInteger.ONE;
                ans=ans.multiply(A(n,n)).multiply(A(m,m));
            }
            System.out.println("Test #"+kase+":");
            System.out.println(ans);

        }
    }
    public static BigInteger A(int n,int m) {
        BigInteger ans=BigInteger.ONE;
        if(n<m) return ans;
        for(int i=1;i<=m;i++) {
            ans=ans.multiply(BigInteger.valueOf(n));
            n--;
        }
        return ans;
    }
    public static BigInteger C(int n,int m) {
        BigInteger ans=BigInteger.ONE;
        if(n<m || m==0) return ans;
        int x=n-m;
        for(int i=1;i<=n;i++)
            ans=ans.multiply(BigInteger.valueOf(i));
        for(int i=1;i<=m;i++)
            ans=ans.divide(BigInteger.valueOf(i));
        for(int i=1;i<=x;i++)
            ans=ans.divide(BigInteger.valueOf(i));
        return ans;
    }
}

猜你喜欢

转载自blog.csdn.net/qq_37685156/article/details/80204233