当前位置: 首页 > news >正文

C++中的容斥原理

C++中的容斥原理——目录

  • C++中的容斥原理:从理论到实践的全面解析
    • 一、容斥原理的基本概念
    • 二、容斥原理的数学公式
    • 三、容斥原理的应用实例
    • 四、容斥原理在C++编程中的实现
    • 五、容斥原理的其他应用
    • 六、总结与展望

C++中的容斥原理:从理论到实践的全面解析

在C++编程和算法设计中,容斥原理(Inclusion-Exclusion Principle)是一种极为重要的计数技巧。它不仅在数学领域有着广泛的应用,还在计算机科学、组合数学以及概率论等多个领域中扮演着关键角色。本文将详细介绍容斥原理的基本概念、公式推导、实际应用以及如何在C++编程中实现这一强大的工具。

一、容斥原理的基本概念

容斥原理,也称为包含-排除原理,是一种用于计算多个集合的并集或交集元素个数的计数方法。其核心思想在于通过逐步加入和排除各个集合的元素,确保每个元素只被计算一次,从而避免重复或遗漏。

简单来说,当我们想要计算多个集合的并集时,直接相加会导致重复计算那些同时属于多个集合的元素。因此,我们需要减去这些重复计算的部分,这就是“排斥”的过程。同理,当我们计算多个集合的交集时,也需要通过类似的方法来确保准确性。

二、容斥原理的数学公式

1. 两个集合的情况

对于两个集合AAABBB,它们的并集大小可以通过以下公式计算:

∣A∪B∣=∣A∣+∣B∣−∣A∩B∣|A \cup B| = |A| + |B| - |A \cap B| AB=A+BAB

其中,∣A∣|A|A∣B∣|B|B分别表示集合AAABBB的元素个数,∣A∩B∣|A \cap B|AB表示AAABBB的交集元素个数。

解释

  • 首先,我们将AAABBB的元素个数相加,得到∣A∣+∣B∣|A| + |B|A+B
  • 但是,这样会把同时属于AAABBB的元素(即∣A∩B∣|A \cap B|AB)计算两次,所以需要减去一次∣A∩B∣|A \cap B|AB,以修正重复计数。

2. 三个集合的情况

对于三个集合AAABBBCCC,它们的并集大小可以通过以下公式计算:

∣A∪B∪C∣=∣A∣+∣B∣+∣C∣−∣A∩B∣−∣B∩C∣−∣A∩C∣+∣A∩B∩C∣|A \cup B \cup C| = |A| + |B| + |C| - |A \cap B| - |B \cap C| - |A \cap C| + |A \cap B \cap C| ABC=A+B+CABBCAC+ABC

解释

  • 首先,我们将AAABBBCCC的元素个数相加,得到∣A∣+∣B∣+∣C∣|A| + |B| + |C|A+B+C
  • 然后,减去每两个集合的交集元素个数,即∣A∩B∣+∣B∩C∣+∣A∩C∣|A \cap B| + |B \cap C| + |A \cap C|AB+BC+AC,以修正两两之间的重复计数。
  • 最后,加上三个集合的交集元素个数∣A∩B∩C∣|A \cap B \cap C|ABC,因为在前面两步中,这部分元素被减去了三次(一次在∣A∣+∣B∣+∣C∣|A| + |B| + |C|A+B+C中,两次在减去两两交集时),所以需要加回一次。

3. nnn个集合的通用公式

对于nnn个集合A1,A2,…,AnA_1, A_2, \dots, A_nA1,A2,,An,它们的并集大小可以通过以下公式计算:

∣⋃i=1nAi∣=∑i=1n∣Ai∣−∑1≤i<j≤n∣Ai∩Aj∣+∑1≤i<j<k≤n∣Ai∩Aj∩Ak∣−⋯+(−1)n+1∣A1∩A2∩⋯∩An∣\left|\bigcup_{i=1}^n A_i\right| = \sum_{i=1}^n |A_i| - \sum_{1 \leq i < j \leq n} |A_i \cap A_j| + \sum_{1 \leq i < j < k \leq n} |A_i \cap A_j \cap A_k| - \dots + (-1)^{n+1} |A_1 \cap A_2 \cap \dots \cap A_n| i=1nAi=i=1nAi1i<jnAiAj+1i<j<knAiAjAk+(1)n+1A1A2An

或者,使用更紧凑的符号表示为:

∣⋃i=1nAi∣=∑S⊆[n],S≠∅(−1)∣S∣+1∣⋂i∈SAi∣\left|\bigcup_{i=1}^n A_i\right| = \sum_{S \subseteq [n], S \neq \emptyset} (-1)^{|S|+1} \left|\bigcap_{i \in S} A_i\right| i=1nAi=S[n],S=(1)S+1iSAi

其中,[n]={1,2,…,n}[n] = \{1, 2, \dots, n\}[n]={1,2,,n}SSS表示[n][n][n]的一个非空子集,∣S∣|S|S表示子集SSS的大小。

解释

  • 这个公式是容斥原理的推广,适用于任意数量的集合。
  • 它通过交替地加上和减去不同大小交集的元素个数,来确保每个元素只被计算一次。
  • 具体来说,对于每个子集SSS,如果SSS的大小是奇数,则加上∣⋂i∈SAi∣\left|\bigcap\limits_{i \in S} A_i\right|iSAi;如果SSS的大小是偶数,则减去∣⋂i∈SAi∣\left|\bigcap\limits_{i \in S} A_i\right|iSAi

三、容斥原理的应用实例

为了更好地理解容斥原理的应用,下面我们通过几个具体的例子来说明。

1. 两个集合的应用实例

问题:一个班级有303030名学生,其中171717人语文成绩超过909090分,252525人数学成绩超过909090分,同时有151515人语文和数学成绩都超过909090分。问有多少名学生两门课都没有超过909090分?

解答

  • AAA为语文成绩超过909090分的学生集合,BBB为数学成绩超过909090分的学生集合。
  • 根据题意,∣A∣=17|A| = 17A=17∣B∣=25|B| = 25B=25∣A∩B∣=15|A \cap B| = 15AB=15
  • 首先计算至少有一门课超过909090分的学生人数:
    ∣A∪B∣=∣A∣+∣B∣−∣A∩B∣=17+25−15=27|A \cup B| = |A| + |B| - |A \cap B| = 17 + 25 - 15 = 27 AB=A+BAB=17+2515=27
  • 然后,用班级总人数减去这个值,得到两门课都没有超过909090分的学生人数:
    30−27=330 - 27 = 3 3027=3
  • 所以,有333名学生两门课都没有超过909090分。

2. 三个集合的应用实例

问题:某班学生中,参加数学竞赛的有252525人,参加物理竞赛的有202020人,参加化学竞赛的有181818人;同时参加数学和物理的有101010人,同时参加数学和化学的有888人,同时参加物理和化学的有666人;三个竞赛都参加的有333人。求至少参加一个竞赛的学生人数。

解答

  • AAA为参加数学竞赛的学生集合,BBB为参加物理竞赛的学生集合,CCC为参加化学竞赛的学生集合。
  • 根据题意,∣A∣=25|A| = 25A=25∣B∣=20|B| = 20B=20∣C∣=18|C| = 18C=18∣A∩B∣=10|A \cap B| = 10AB=10∣B∩C∣=6|B \cap C| = 6BC=6∣A∩C∣=8|A \cap C| = 8AC=8∣A∩B∩C∣=3|A \cap B \cap C| = 3ABC=3
  • 使用容斥原理计算至少参加一个竞赛的学生人数:
    ∣A∪B∪C∣=∣A∣+∣B∣+∣C∣−∣A∩B∣−∣B∩C∣−∣A∩C∣+∣A∩B∩C∣=25+20+18−10−6−8+3=42|A \cup B \cup C| = |A| + |B| + |C| - |A \cap B| - |B \cap C| - |A \cap C| + |A \cap B \cap C| = 25 + 20 + 18 - 10 - 6 - 8 + 3 = 42 ABC=A+B+CABBCAC+ABC=25+20+181068+3=42
  • 所以,至少参加一个竞赛的学生有424242人。

3. nnn个集合的应用实例

问题:给定一组整数,求其中所有互质的数对的数量。

解答

  • 这个问题可以转化为求所有不互质的数对的数量,然后用总数减去这个值。
  • 首先,计算所有数对的总数,即C(n,2)=n(n−1)2C(n, 2) = \frac{n(n-1)}{2}C(n,2)=2n(n1)
  • 然后,使用容斥原理计算所有不互质的数对的数量。这需要找出所有具有公共因子的数对,并减去重复计算的部分。
  • 具体来说,可以先找出所有具有某个特定因子的数对,然后减去同时具有两个特定因子的数对,再加上同时具有三个特定因子的数对,依此类推。
  • 这个例子展示了容斥原理在处理复杂计数问题时的强大能力。

四、容斥原理在C++编程中的实现

在C++编程中,我们可以通过编写函数来实现容斥原理的计算。下面是一个通用的C++函数模板,用于计算任意数量集合的并集大小。

#include <iostream>
#include <vector>
#include <functional>
#include <cmath>// 定义一个通用的容斥原理计算函数
template<typename T>
T inclusionExclusion(const std::vector<std::vector<int>>& sets, const std::vector<T>& sizes, int n) {T result = 0;// 遍历所有可能的子集for (int mask = 1; mask < (1 << n); ++mask) {int bits = __builtin_popcount(mask); // 计算子集中元素的数量T intersectionSize = sizes[bits]; // 获取对应大小的交集元素个数// 根据子集大小的奇偶性决定加还是减if (bits % 2 == 1) {result += intersectionSize;} else {result -= intersectionSize;}}return result;
}int main() {// 示例:三个集合的情况std::vector<std::vector<int>> sets = {{1, 2, 3}, {2, 3, 4}, {3, 4, 5}}; // 示例集合(这里仅示意,实际应为交集大小)std::vector<int> sizes = {0, 3, 2, 1}; // 对应子集大小的交集元素个数(这里仅为示例)int n = 3; // 集合数量// 调用容斥原理计算函数int result = inclusionExclusion(sets, sizes, n);std::cout << "The size of the union is: " << result << std::endl;return 0;
}

注意:上述代码中的setssizes参数需要根据实际问题进行定义和计算。sets表示各个集合的元素(在实际问题中可能需要根据交集大小来调整),sizes表示不同大小子集的交集元素个数。在实际应用中,可能需要先计算出这些交集的大小,然后再调用inclusionExclusion函数进行计算。

此外,C++标准库中并没有直接提供容斥原理的实现函数,但我们可以根据自己的需求编写类似的函数来解决问题。例如,可以使用位运算来遍历所有可能的子集,并根据子集大小的奇偶性来决定是加还是减对应的交集元素个数。

五、容斥原理的其他应用

除了上述的基本应用外,容斥原理还可以用于解决一些更复杂的问题。例如:

  • 概率论中的复合事件概率计算:通过容斥原理可以计算多个事件同时发生的概率。
  • 组合数学中的排列组合问题:在某些排列组合问题中,可以使用容斥原理来排除不符合条件的情况。
  • 图论中的路径计数问题:在计算图中两点之间所有可能的路径数时,如果某些边不能同时经过,可以使用容斥原理来修正计数。
  • 动态规划中的优化问题:在某些动态规划问题中,可以利用容斥原理来减少状态转移的次数或优化计算过程。

六、总结与展望

容斥原理作为一种强大的计数工具,在C++编程和算法设计中有着广泛的应用。通过掌握其基本概念、数学公式以及实际应用方法,我们可以更加高效地解决各种复杂的计数问题。同时,随着计算机科学的不断发展和应用领域的不断拓展,容斥原理也将在未来发挥更加重要的作用。希望本文能够为广大C++开发者和算法爱好者提供有益的参考和帮助。

http://www.dtcms.com/a/275834.html

相关文章:

  • css 判断是ios设备 是Safari浏览器
  • 敏捷开发方法全景解析
  • vue2和vue3的响应式原理
  • 【Datawhale AI 夏令营】 用AI做带货视频评论分析(二)
  • npgsql/dapper/postgresql的时区问题
  • 深入解析 LinkedList
  • Windows去除管理员弹窗确认
  • Claude code在Windows上的配置流程
  • 【6.1.0 漫画数据库技术选型】
  • Linux系统中安装mysql详解
  • 计算机毕业设计springboot扶贫助农与产品合作系统 基于SpringBoot的农村电商助农平台设计与实现 乡村振兴背景下的农产品对接与帮扶管理系统
  • C语言课程设计--电子万年历
  • 【数据分析】03 - Matplotlib
  • 9.2 埃尔米特矩阵和酉矩阵
  • Go内存分配
  • linux系统mysql性能优化
  • 【Modern C++ Part9】Prefer-alias-declarations-to-typedefs
  • Opencv---深度学习开发
  • 云计算三大服务模式深度解析:IaaS、PaaS、SaaS
  • 【数据结构与算法】数据结构初阶:详解顺序表和链表(四)——单链表(下)
  • 【PTA数据结构 | C语言版】后缀表达式求值
  • Transforms
  • Spring(四) 关于AOP的源码解析与思考
  • 一文理解缓存的本质:分层架构、原理对比与实战精粹
  • 别再怕 JSON!5分钟带你轻松搞懂这个程序员的好帮手
  • 鸿蒙的NDK开发初级入门篇
  • RISC-V:开源芯浪潮下的技术突围与职业新赛道 (四) 产业应用全景扫描
  • (LeetCode 面试经典 150 题 ) 209. 长度最小的子数组(双指针)
  • Ntfs!LfsFlushLfcb函数分析之while的循环条件NextLbcb的确定和FirstLbcb->LbcbFlags的几种情况
  • docker-compose方式搭建lnmp环境——筑梦之路