排列组合
public class BaseDemo {
/**
* 在 XxY 的方格中,以左上角格子为起点,右下角格子为终点,
* 每次只能向下走或者向右走,请问一共有多少种不同的走法
* 给定两个正整数 int x,int y,请返回走法数目。保证 x+y 小于等于 12。
*/
public int countWays(int x, int y) {
//总的步数
int sum = x + y - 2;
//向下的步数,这里也可以是 right = x-1
int down = y - 1;
return fac(sum) / (fac(down) * fac(sum - down));
}
// 阶乘函数
private int fac(int num) {
int sum = 1;
for (int i = 1; i <= num; i++) {
sum *= i;
}
return sum;
}
/**
* n 个人站队,他们的编号依次从 1 到 n,
* 要求编号为 a 的人必须在编号为 b 的人的左边,但不要求一定相邻,
* 请问共有多少种排法?第二问如果要求 a 必须在 b 的左边,并且一定要相邻,请问一共有多少种排法?
* 给定人数 n 及两个人的编号 a 和 b,请返回一个两个元素的数组,其中两个元素依次为两个问题的答案。
* 保证人数小于等于 10。
*/
public int[] getWays(int n, int a, int b) {
// 非左即右,答案为所有组合结果除以二
// 两人绑定为一个人,答案为少了一个人的所有组合结果
return new int[]{
fac(n) / 2, fac(n - 1)};
}
/**
* A (A 也是他的编号) 是一个孤傲的人,在一个 n 个人 (其中编号依次为 1 到 n) 的队列中,
* 他于其中的标号为 b 和标号 c 的人都有矛盾,所以他不会和他们站在相邻的位置。
* 现在问你满足 A 的要求的对列有多少种?
* 给定人数 n 和三个人的标号 A,b 和 c,请返回所求答案,保证人数小于等于 11 且大于等于 3。
*/
public int getWays(int n, int A, int b, int c) {
// 所有组合
int sum = fac(n);
// 与 c 一起的所有组合(左边 + 右边)
int ac = fac(n - 1) * 2;
// 与 b 一起的所有组合(左边 + 右边)
int ab = fac(n - 1) * 2;
// ac 与 ab 的重复(abc 视作一个人)
int overlap = fac(n - 2) * 2;
return sum - ac - ab + overlap;
}
/**
* n 颗相同的糖果,分给 m 个人,每人至少一颗,问有多少种分法。
* 给定 n 和 m,请返回方案数,保证 n 小于等于 12,且 m 小于等于 n。
*/
public int getWays(int n, int m) {
// n 个糖,n-1 个空隙,m 个人, m-1 个隔板
// C^{m-1}_{n-1}
return fac