zoukankan      html  css  js  c++  java
  • 数学知识 --- 组合数

    什么是组合数?

    • 组合数公式是指从n个不同元素中,任取m(m≤n)个元素并成一组,叫做从n个不同元素中取出m个元素的一个组合;
    • 从n个不同元素中取出m(m≤n)个元素的所有组合的个数,叫做n个不同元素中取出m个元素的组合数。用符号c(n,m) 表示

    前置知识:排列公式

    • 排列公式是建立一个模型,从n个不相同元素中取出m个排成一列(有序)
    • 第一个位置可以有n个选择,第二个位置可以有n-1个选择(已经有1个放在前一个位置),则同理可知第三个位置可以有n-2个选择,以此类推第m个位置可以有n-m+1个选择

    组合数公式

    • 组合公式的推导是由排列公式去掉重复的部分而来的
    • 组合公式对应另一个模型,取出m个成为一组(无序)
    • 排列公式建立有序,而组合公式建立为无序,有序情况当然比无序多

    通过递推来求组合数,时间复杂度为O(N^2)

    c(n,m)=c(n-1,m-1)+c(n-1,m)

    • 从n中选m个元素可以选取某元素的包含与不包含,分成两类情况,即m个被选择元素包含了这个元素和m个被选择元素不包含该元素。
    • 前者相当于从n-1个元素中选出m-1个元素的组合,即c(n-1,m-1)
    • 后者相当于从n-1个元素中选出m个元素的组合,即c(n-1,m)

    代码如下:

    • 预处理所有的情况,a,b范围在[1,2000]
    #include<iostream>
    
    using namespace std;
    
    const int N = 2e3 + 10,mod = 1e9 + 7;
    
    int n;
    int a,b;
    int c[N][N];
    
    void init(){
        for(int i = 0; i < N; i ++ ){
            for(int j = 0; j <= i; j ++ ){
                if(!j) c[i][j] = 1;
                else c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
            }
         }
    }
    int main(){
        init();
        cin >> n;
        while(n -- ){
            cin >> a >> b;
            cout<<c[a][b]<<endl;
        }
        return 0;
    }
    

    通过组合数公式求组合数,时间复杂度为O(nlogn)

    • 用fact[]数组来存放(n!)mod(1e9 + 7)
    • 用infact[]数组来存放(n!)的逆元mod(1e9 + 7)
    • 求逆元用快速幂和费马小定理
    • 组合数为:fact[a] * infact[b - a] * infact[b]
    • 防止超出范围:fact[a] * infact[b] % mod * infact[a - b] % mod

    代码如下:

    • 预处理,a,b范围在[1,1e5]
    #include<iostream>
    
    using namespace std;
    
    #define ll long long
    const int N = 1e5 + 10,mod = 1e9 + 7;
    
    int n;
    ll fact[N],infact[N];
    
    ll qmi(ll a,ll b,ll p){
    	ll res = 1%p;
    	while(b){
    		if(b & 1) res = res * a % p;
    		a = a * a % p;
    		b >>= 1; 
    	}
    	return res;
    }
    
    void init(){
    	fact[0] = infact[0] = 1;
    	for(int i = 1; i < N; i ++ ){
    		fact[i] = fact[i - 1] * i % mod;
    		infact[i] = infact[i - 1] * qmi(i,mod - 2,mod) % mod;
    	}
    }
    int main(){
    	cin >> n;
    	while(n -- ){
    		int a,b;
    		cin >> a >> b;
    		printf("%lld
    ",fact[a] * infact[b] % mod * infact[a - b] % mod);
    	}    
        return 0;
    }
    
  • 相关阅读:
    sqlserver中实现split分割字符串函数
    Sqlserver判断对象是否存在
    C#学习笔记(十):阴影和深度复制 [转]
    C#企业库自定义连接字符串.
    Jquery error 事件
    SQLserver不是可以识别的内置函数名称"的错误,通过set statistics time on能得到SQL语句的执行时间精确到毫秒.
    C#学习笔记(九):c#运算符重载.[简单理解]
    利用sql语句添加字段注释
    如何抓取google的搜索结果?
    SQL SERVER级联删除的简单实现方法
  • 原文地址:https://www.cnblogs.com/bingers/p/14088836.html
Copyright © 2011-2022 走看看