hdu1133 Buy the Ticket(卡特兰数变形n个入栈m个出栈问题)

Buy the Ticket
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 8673 Accepted Submission(s): 3622

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

Author
HUANG, Ninghai

方法一:
n为50的个数,m为100的个数。
n需要>=m
合法数=总数-不合法数
总数=n+m个中取n个:C(n+m,n)
不合法数=n+m中取得较少的50的个数比m还小就是m-1100就是多一点就是n+1,所以为C(n+m,n+1)
推出公式:C(n+m,n)=C(n+m,n)-C(n+m,n+1);

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

用java大数循环求阶乘即可。

import java.io.*;  
import java.math.BigInteger;  
import java.util.*; 
 
public class Main {
	
    public static void main(String[] args) {
    	Scanner scanner = new Scanner(System.in);
    	int n,m;
    	int cnt=0;
    	while(scanner.hasNext())
    	{
    		cnt++;
    		n=scanner.nextInt();
    		m=scanner.nextInt();
    		if(n==0&&m==0)
    			break;
    		if(n<m){
    		System.out.println("Test #"+cnt+":");
    		System.out.println(0);
    			continue;
    		}
    		BigInteger ans=BigInteger.ONE;
    		for(int i=1;i<=n+m;i++)
    		{
    			ans=ans.multiply(BigInteger.valueOf(i));
    		}
    		ans=ans.multiply(BigInteger.valueOf(n-m+1)).divide(BigInteger.valueOf(n+1));
    		System.out.println("Test #"+cnt+":");
    		System.out.println(ans);
     
    		
    	}
     }
}
方法二:
题目分析,有n+m个人去买票,m个人拿50,n个人拿100,问有几种安排人排队的方法。明显是一个有关组合的问题,我们先判断一下特殊情况,只有在m>=n的情况下才可能安排排队,否则钱是找不开的,而且n=0时全是拿50的,那直接n的阶乘就好了。在一般情况下,可以列一个n*m的矩阵来表示答案,而且因为只要有几种排队方法确定以后直接乘以n的阶乘和m的阶乘就可以了,所以我们可以把n个人视为一样的,m个人视为一样的。那么就可以确定一般情况时排队方法有多少种。

 则第m+n个人的排队方式可以看做多了第m+n个人,本来已经有了(m+n-1)个人,如果这个人拿的是50,那么就是在((m-1+n)的基础上多了一个人,此时第m+n个人站在最后面(因为每个人都一样,所以实际上已经考虑了所有的情况),同样,如果这个人拿的是100,那么就是在(m+(n-1))的基础上多了一个人,因为人 都一样,所以又有(m,n-1)这种情况的种类,那么第m+n个人的排队类数就是(m,n-1)和(m-1,n)的和,(事实上如果最后来的那个人不站最后面那么就会出现重复的排队数,你可以试试用笔推一下)。那么递推式就出来了,我们就可以用打表的方法利用递推把m,n个人对应的排队数目用数组存储起来

```n+m的时候本状态:第n+m个是100的状态+第n+m是50的状态
递推到前面的状态:
第n+m个是100的状态=第n+(m-1)的状态
第n+m个是50的状态=(n-1)+m的状态
dp[i][j]=dp[i-1][j]+dp[i][j-1]
									
dp[i][j]:i个50,j个100的排队方案
如果第i+j个数是50,则前一个数有i-150,j个100;如果第i+j个数是100,那么前一个数有i个50,j-1100;
所以dp[i][j]=dp[i-1][j]+dp[i][j-1]
i<j时说明50不够为0,
j=0时说明全是50方案数为n!但是这里先当做同一个数处理,为1;
最后所有的不考虑顺序的方案数再考虑顺序,n全排列,m全排列,乘以n!*m!**
import java.io.*;  
import java.math.BigInteger;  
import java.util.*; 
 
public class Main {
	
    public static void main(String[] args) {
    	Scanner scanner = new Scanner(System.in);
    	int n,m;
    	int cnt=0;
    	BigInteger dp[][] =new BigInteger[200][200];
    	for(int i=0;i<=100;i++){
			for(int j=0;j<=100;j++)
			{
				if(i<j)dp[i][j]=BigInteger.ZERO;
				else if(j==0) dp[i][j]=BigInteger.ONE;
				else
				dp[i][j]=dp[i-1][j].add(dp[i][j-1]);
			}
		}
    	while(scanner.hasNext())
    	{
    		cnt++;
    		n=scanner.nextInt();
    		m=scanner.nextInt();
    		if(n==0&&m==0)
    			break;
    		
    		//System.out.println(1);
    		BigInteger ans=BigInteger.ONE;
    		for(int i=1;i<=n;i++)
    		{
    			ans=ans.multiply(BigInteger.valueOf(i));
    		}
    		for(int i=1;i<=m;i++)
    		{
    			ans=ans.multiply(BigInteger.valueOf(i));
    		}
    		ans=ans.multiply(dp[n][m]);
    		//ans=ans.multiply(BigInteger.valueOf(n-m+1)).divide(BigInteger.valueOf(n+1));
    		System.out.println("Test #"+cnt+":");
    		System.out.println(ans);
     
    		
    	}
     }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值