这是一道经典的好题,本来是冲着高精度去练习Java的,却发现并非那么简单,其中涉及到了数论的知识,看了多个博客的解释才粗略了解,其实还是没能理解公式的推导,这里拜托@Band能帮忙做个简单的证明~~我看在这题涉及了比较多Java的BigInteger类的应用,还是写出来。
题意:有m个人持有50元,有n个人持有100元,他们排队买价格为50元的Ticket,问有多少种排列方式使得售票处可以顺利找零(售票处一开始没有任何零钱)。
根据卡特兰数列推导出的公式为:( C(m+n, m) – C(m+n, m+1) ) * m! * n! (待解释)
代码:
import java.io.*;
import java.util.*;
import java.math.*;
public class Main {
public static void main(String args[]) {
BigInteger[] fact = new BigInteger[110];
fact[0] = BigInteger.ONE;
for (int i = 1; i <= 100; i++)
fact[i] = fact[i-1].multiply(BigInteger.valueOf(i));
// 预处理阶乘
int test = 1, n, m;
Scanner cin = new Scanner(System.in);
while (cin.hasNext()) {
m = cin.nextInt();
n = cin.nextInt();
if (n == 0 && m == 0) break;
System.out.println("Test #" + test++ + ":");
if (n > m)
System.out.println("0");
else {
BigInteger ans = fact[m].multiply(fact[n]);
ans = ans.multiply(C(n+m, m).subtract(C(n+m, m+1)));
System.out.println(ans);
}
}
}
public static BigInteger C(int n, int m) {
BigInteger tmp = BigInteger.ONE;
for (int i = 0; i < m; i++)
tmp = tmp.multiply(BigInteger.valueOf(n-i));
for (int i = 2; i <= m; i++)
tmp = tmp.divide(BigInteger.valueOf(i));
return tmp;
}
}
首先感谢Band,一席话令我恍然大悟,这题其实也可以用DP来做的,思路来得更直观!题目是m个人持有50元,n个人持有100元,首先计算!m * !n,这样一来所有持有相同金额的人就可以看做互相没有任何区别的人了。
dp[i][j]表示当前有i个人持有50元、j个人持有100元的合法排列总数,易知当i < j时,dp[i][j] = 0。当j = 0时,dp[i][j] = 1。剩下的情况如下计算:
dp[i][j] = dp[i-1][j] + dp[i][j-1]。
假设最后一个人持有的是50元,那么前边的i-1个持有50元的人和j个持有100元的人必须构成合法排列,可以表示为dp[i-1][j]。
假设最后一个人持有的是100元,同理,可以表示为dp[i][j-1]。相加即是答案。很明显数据需要记忆化一下,第一次用Java写记忆化搜索,写得很爽啊~
代码:
// 156 Ms
import java.io.*;
import java.util.*;
import java.math.*;
public class Main {
public static void main(String args[]) {
BigInteger dp[][] = new BigInteger[110][110];
boolean flag[][] = new boolean[110][110];
BigInteger fact[] = new BigInteger[110];
Ini(flag, fact);
int Test = 1;
Scanner cin = new Scanner(System.in);
while (cin.hasNext()) {
int m = cin.nextInt();
int n = cin.nextInt();
if (m == 0 && n == 0) break;
BigInteger ans = fact[m].multiply(fact[n]);
ans = ans.multiply(DFS(dp, flag, m, n));
System.out.println("Test #" + Test++ + ":");
System.out.println(ans);
}
}
public static void Ini(boolean flag[][], BigInteger fact[]) {
for (int i = 0; i <= 100; i++) {
for (int j = 0; j <= 100; j++)
flag[i][j] = false;
}
// flag数组初始化
fact[0] = BigInteger.ONE;
for (int i = 1; i <= 100; i++)
fact[i] = fact[i-1].multiply(BigInteger.valueOf(i));
// 预处理阶乘
}
public static BigInteger DFS(BigInteger dp[][], boolean flag[][], int m, int n) {
// 记忆化搜索
if (flag[m][n] == true) return dp[m][n];
if (m < n) return BigInteger.ZERO;
if (n == 0) return BigInteger.ONE;
BigInteger a = DFS(dp, flag, m, n-1);
BigInteger b = DFS(dp, flag, m-1, n);
dp[m][n] = a.add(b);
flag[m][n] = true;
return dp[m][n];
}
}