数据结构中的排列组合 —— Java实现

Java中的排列组合

排列组合

这个概念高中已经学过了,排列(A)就是不看顺序,比如2,1和1,2算一种情况。而组合(C)就是看顺序,上述例子算两种情况。下面给出排列和组合的数学公式:

  • A (nm ) = n! / m!【排列】
  • C (nm) = n! / (m! * (n-m)!)【组合】

组合

杨辉三角预处理组合数

  • 杨辉三角的基本性质:
    第n行第m列的值等于组合数(n m
    边界条件:(n0) = (nn) = 1
    递推关系:(nm) = (n-1m)+(n-1m-1
  • 基础预处理
java 复制代码
// 初始化
int[][] C = new int[MAX][MAX];
for (int i = 0; i < MAX; i++) {
    C[i][0] = 1;
    C[i][i] = 1;
}

// 递推填充
for (int i = 2; i < MAX; i++) {
    for (int j = 1; j < i; j++) {
        C[i][j] = C[i-1][j] + C[i-1][j-1];
    }
}

例题1(FROM 洛谷 P2822)

代码实现

java 复制代码
import java.util.*;
public class Main{
    public static void main(String [] args){
        Scanner input = new Scanner (System.in);
        int t = input.nextInt();
        int k = input.nextInt();
        int MAX = 2001;//题没截取全,最后写了,数据最大会是2000,所以开2001
        int [][] c = new int [MAX+1][MAX+1];
        int [][] ans = new int [MAX+1][MAX+1];
        
//------------------------------------------------基础预处理------------------------------------------------------------------------------------------------------------------
        for(int i=0;i<MAX;i++){
            c[i][0] = 1 % k;//如果直接写1,当k=1时,1%1=0
            c[i][i] = 1 % k;
        }
        for(int i=2;i<=MAX;i++)
            for(int j=1;j<=i;j++)
                c[i][j] = (c[i-1][j] + c[i-1][j-1]) % k;
                
//---------------------------------------------------前缀和处理---------------------------------------------------------------------------------------------------------------
        int [][] rowPre = new int [MAX+1][MAX+1];
        for(int i=0;i<=MAX;i++)
            for(int j=0;j<=MAX;j++){
                rowPre[i][j] = (j == 0)?0:rowPre[i][j-1];
                if(j <= i && c[i][j] == 0)
                    rowPre[i][j] ++;
            }
//---------------------------------------------------答案数组处理---------------------------------------------------------------------------------------------------------------            
        for(int j = 0;j<=MAX;j++)
            for(int i=0;i<=MAX;i++){
                if(i == 0)
                    ans[i][j] = rowPre[i][j];
                else ans[i][j] = ans[i-1][j] + rowPre[i][j];
            }
//---------------------------------------------------输出处理------------------------------------------------------------------------------------------------------------------------------
        for(int i = 0;i<t;i++){
            int n = input.nextInt();
            int m = input.nextInt();
            System.out.println(ans[n][m]);
        }
    }
}

排列

在排列中,涉及到许多阶乘,阶乘的循环处理太过耗费时间,所以我们可以使用阶乘数组 来预处理。同时在计算排列数的时候,如果我们需要取模,就不可以单纯的使用除法,因为模运算中没有除法的分配律,这时候我们就要用到阶乘逆元数组

阶乘数组

java 复制代码
static long [] fac = new long [MAX];
fac[0] = 1;//规定 0!= 1
for(int i=1;i<MAX;i++)
	fac[i] = fac[i-1] * i;

阶乘逆元数组

逆元类似于数学中的倒数,阶乘的逆元数组通常在模运算中使用,比如模数为7,则3 的逆元是 5,因为 3 * 5 = 15 ,15 % 7 = 1 ,如果存在b可以使(a*b)% mod = 1,则a与b互为逆元。

  • 小费马定理
    invFac[MAXN-1] = fac[MAXN-1]^(MOD-2) % MOD
    invfac[i] = (invfac[i+1]*(i+1))
java 复制代码
static long [] invfac = new long [MAX];
invfac[MAX-1] = pow(fac[MAX-1],MOD-2);//pow是自定义快速幂函数
for(int i=MAX-2;i>=0;i--)
	invfac[i] = (invfac[i+1]*(i+1)) % MOD;

例题1(FROM 洛谷 P4071)

有个问题非常棘手,由于Java的运行内存比C++大的多,所以我拼尽全力优化了所有也无法全部AC,总会有MLE。(AI也没招了......)大家如果有可以全部AC的方法可以发出来,我将逐字学习!

思路

在这道题中要求有多少种方式,我们从数学角度先将其建模。首先"满足恰好有m个位置使得 ai = i",所以我们要从n个数里取出m个数固定,这个取法有C(nm)种。然后我们排列 其他未固定的数字,值得注意的是:未固定数字不能再放在其原本的位置上 ,于是我们要用到错排。

  • 错排
    定义:n个数排列且所有数字都不在原有位置上的情况数
    初始化:D[0] = 1空排列视为合法错排,D[1] = 0只有一个元素无法错排
    递推公式:D(n) = (n-1) * ( D(n-1) + D(n-2) )

代码实现

java 复制代码
import java.util.*;
import java.io.*;

public class Main{
    static BufferedReader bf = new BufferedReader (new InputStreamReader (System.in));
    static StringTokenizer st;
    static int MOD = 1000000000 + 7;
    static int MAX = 1000000 + 1;
    static long [] fac = new long [MAX];
    static long [] invfac = new long [MAX];
    static long [] D = new long [MAX];
    public static void main(String [] args)throws IOException{
        init();
        int T = Integer.parseInt(next());
        StringBuilder sb = new StringBuilder();
        while(T>0){
            int n = Integer.parseInt(next());
            int m = Integer.parseInt(next());
            if(m>n){
                System.out.print(0);
                continue;
            }
            long c = C(n,m);
            long d = D[n-m];
            long ans = (c*d)%MOD;
            sb.append(ans).append("\n");
            T--;
        }
        System.out.print(sb.toString());
    }
    static void init(){
        fac[0] = 1;
        for(int i=1;i<MAX;i++)
            fac[i] = (fac[i-1] * i) % MOD;
        
        // 预处理阶乘逆元 - 使用费马小定理
        // invFac[MAXN-1] = fac[MAXN-1]^(MOD-2) % MOD
        invfac[MAX-1] = pow(fac[MAX-1],MOD-2);
        for(int i=MAX-2;i>=0;i--)
            invfac[i] = (invfac[i+1]*(i+1)) % MOD;

        D[0] = 1;
        D[1] = 0;
        for(int i=2;i<MAX;i++)
            D[i] = ((long)(i-1)*(D[i-1]+D[i-2]))%MOD;
    }
    static long C(int n,int m){
        if(m>n || m<0) return 0;
        return ((fac[n] * invfac[m])%MOD * invfac[n-m]) % MOD;
    }
    static long pow(long a,long b){
        long res = 1;
        while(b > 0){
            if( (b&1) == 1) {
                res *= a;
                res %= MOD;
            }
            a *= a;
            a %= MOD;
            b>>=1;
        }
        return res;
    }
    static String next() throws IOException{
        while(st == null||!st.hasMoreTokens()){
            String str = bf.readLine();
            if(str == null) return null;
            st = new StringTokenizer(str);
        }
        return st.nextToken();
    }
}
相关推荐
大熊背2 小时前
ISP Pipeline中Lv实现方式探究之三--lv计算定点实现
数据结构·算法·自动曝光·lv·isppipeline
初夏睡觉2 小时前
c++1.3(变量与常量,简单数学运算详解),草稿公放
开发语言·c++
升职佳兴2 小时前
C盘爆满自救:3步无损迁移应用数据到E盘(含回滚)
c语言·开发语言
ID_180079054733 小时前
除了 Python,还有哪些语言可以解析 JSON 数据?
开发语言·python·json
周末也要写八哥3 小时前
多进程和多线程的特点和区别
java·开发语言·jvm
惜茶4 小时前
vue+SpringBoot(前后端交互)
java·vue.js·spring boot
宁瑶琴4 小时前
COBOL语言的云计算
开发语言·后端·golang
杰克尼4 小时前
springCloud_day07(MQ高级)
java·spring·spring cloud
小陈工5 小时前
2026年4月2日技术资讯洞察:数据库融合革命、端侧AI突破与脑机接口产业化
开发语言·前端·数据库·人工智能·python·安全