C++中求组合数的各种方法总结详解

C++中求组合数的各种方法总结详解

前言

组合数问题在许多算法问题中都有广泛应用,在C++中求组合数的方法也多种多样。本文将总结并详细解释C++中求组合数的各种方法。

直接递推法

组合数的定义式为:$C_{n}^{m}=\frac{n!}{m!(n-m)!}$,可以通过递归的方法直接求解。

递归式为:$C_{n}^{m}=C_{n-1}^{m-1}+C_{n-1}^{m}$

具体实现代码如下:

int C(int n, int m)
{
    if (m == 0 || n == m)
        return 1;
    else
        return C(n - 1, m - 1) + C(n - 1, m);
}

该方法简单易懂,但运行时间可能比较长,特别是当n和m较大时,时间复杂度为O($2^n$)。

非递归的递推法

通过分析递推公式,我们可以发现$C_{n}^{m}$只与$C_{n-1}^{i}(i\leq m)$有关。

所以可以通过非递归的方法提高运算速度。

具体实现代码如下:

const int MAXN = 1005;
const int MOD = 1e9 + 7;

int C[MAXN][MAXN];

void init()
{
    for (int i = 0; i < MAXN; i++)
    {
        C[i][0] = 1;
        for (int j = 1; j <= i; j++)
            C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % MOD;
    }
}

int main()
{
    init();
    cout << C[10][5] << endl; //输出组合数C10,5
    return 0;
}

该方法通过预先计算组合数,可以大大减少递推计算量,运行时间为$O(n^2)$。

Lucas定理

Lucas定理是一种用于解决组合数取模问题的算法。它是利用了组合数的整除性质来实现的。

Lucas定理为:$C_{m}^{n}\ \mbox{mod}\ p=C_{m\ \mbox{mod}\ p}^{n\ \mbox{mod}\ p}\cdot C_{(\lfloor\frac{m}{p}\rfloor) }^{(\lfloor\frac{n}{p}\rfloor) }$

具体实现代码如下:

const int MAXN = 1005;
const int MOD = 1e9 + 7;

int C(int n, int m, int p)
{
    if (m == 0 || n == m) return 1;
    int a[MAXN], b[MAXN];
    for (int i = 1; i <= p; i++)
        if (i % p) a[i] = a[i - 1] * i % p;
    b[p - 1] = ksm(a[p - 1], p - 2, p);
    for (int i = p - 2; i >= 1; i--)
        if ((i + 1) % p) b[i] = b[i + 1] * (i + 1) % p;
    int res = 1;
    while (n && m)
    {
        int n1 = n % p, m1 = m % p;
        if (n1 < m1) return 0;
        res *= a[n1] * b[m1] % p * b[n1 - m1] % p;
        res %= p;
        n /= p, m /= p;
    }
    return res;
}

int main()
{
    cout << C(10, 5, MOD) << endl; //输出组合数C10,5
    return 0;
}

该方法在求解组合数时,会先将n、m、p转化为p进制,然后分别计算每一位上对应组合数的值,最后通过有限域上的计算方法(同余性质)得到答案。时间复杂度为$O(p\log _p n)$,具体计算时间随p的取值有关。

特别提示

特别提示:在进行组合数计算时,如果数据范围较大,一定要取模防止溢出。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C++中求组合数的各种方法总结详解 - Python技术站

(0)
上一篇 2023年5月22日
下一篇 2023年5月22日

相关文章

  • 浅谈C++中各种不同意义的new和delete的使用

    浅谈C++中各种不同意义的new和delete的使用 new和delete的基础用法 在C++中,我们可以使用new关键字来动态地为对象分配内存,使用delete关键字来释放该内存。通常的使用方式如下: int* p = new int; // 为一个int类型的数据分配内存空间并返回指向该内存的指针 *p = 10; // 对该内存空间进行赋值 delet…

    C 2023年5月22日
    00
  • 源码分析系列之json_encode()如何转化一个对象

    以下是详细讲解“源码分析系列之json_encode()如何转化一个对象”的完整攻略。 1. 前言 在PHP中,json_encode()函数可以将数组、对象等类型的数据转化为JSON格式的字符串,开发者在进行Web应用程序开发时经常会用到它。 本文将从源码的角度,分析json_encode()函数是如何将PHP对象转化为JSON格式的字符串的。 2. 基础…

    C 2023年5月23日
    00
  • 详解C++ 模板编程

    详解C++ 模板编程攻略 什么是模板编程 模板编程是一种C++编程技术,利用它可以编写具有通用性和可重用性的代码。使用模板编程技术,我们可以让我们的代码更加灵活且容易扩展。 模板编程主要依托于C++的模板(template)机制,通过在编译期间对类型参数进行自动推导,以实现代码的通用性和类型无关性。 模板的解析 在C++中,我们可以通过template来声明…

    C 2023年5月23日
    00
  • 利用Jackson解析JSON的详细实现教程

    下面我将为你详细讲解利用Jackson解析JSON的实现教程。 一、Jackson解析库 Jackson是一个高效的JSON解析库,它可以快速方便地将JSON解析成Java对象,也可以将Java对象转换成JSON格式的字符串。Jackson支持多种数据格式,包括:JSON、XML、YAML等。但在本文中,重点介绍其JSON解析的应用。 Jackson主要由以…

    C 2023年5月23日
    00
  • 简单谈谈C++ 中指针与引用

    下面是关于C++中指针与引用的详细讲解: 指针与引用简介 在C++中,指针和引用都属于变量地址的概念,它们可以被用来实现对变量的间接访问。指针是一个变量,存储着另一个变量的地址,而引用则是一个别名,是被引用变量的另一个名称。 指针和引用都是C++中重要的概念,尤其是在使用函数传参和动态内存分配时,它们常被使用。 指针的使用 在C++中,可以使用指针来实现对另…

    C 2023年5月23日
    00
  • C++面向对象编程之析构详解

    C++面向对象编程之析构详解 概述 在C++面向对象编程中,析构函数是一种特殊的成员函数,它在对象被销毁时调用。析构函数通常用于在对象被销毁前,释放对象所占用的资源,如动态分配的内存空间、文件句柄等。 析构函数的函数名与类名相同,但前面加上 “~” 符号,且析构函数没有返回值和参数。 class MyClass { public: MyClass(); ~M…

    C 2023年5月22日
    00
  • Golang 的defer执行规则说明

    当前站点为标准的Markdown格式化文本提供支持。Markdown是一种轻量级的标记语言,通常由程序员和写作者使用,以便轻松将文本转换为HTML。 Golang 的defer执行规则说明 什么是defer defer是Golang中一个非常有用的关键字,用于确保函数调用在程序执行完当前代码块之后执行。defer被经常用于处理控制流,资源清理等任务,它为代码…

    C 2023年5月23日
    00
  • python json.dumps() json.dump()的区别详解

    当我们需要将Python对象转换为JSON字符串时,我们可以使用Python内置的json模块。在使用json模块时,json.dumps()和json.dump()是两个常用的方法。它们之间有明显的区别,请看下文详解。 json.dumps() json.dumps()方法用来将Python对象转换为JSON格式的字符串,并返回生成的字符串,该方法的语法如…

    C 2023年5月23日
    00
合作推广
合作推广
分享本页
返回顶部