CC++ & Algorithm

C++加法原理——把不同的事情加起来

困难18
语言版本:C++Python
概述:加法原理告诉我们,如果完成一件事有几种不同的方法,那么总方法数就是各种方法数加起来。

加法原理:把不同的事情加起来——轻松搞定分类计数

你有没有遇到过这种情况:今天中午去食堂吃饭,主食有米饭、面条、馒头三种,配菜有红烧肉、番茄鸡蛋、青菜豆腐三种,汤有紫菜蛋花汤和番茄汤两种。你想选一个“主食+配菜+汤”的套餐?那是乘法原理,我们以后再说。但如果你只想知道一共有多少种主食可以选,那很简单:3种。要是你想知道“今天你能吃多少种完全不同类型的午餐”?比如:你可以只吃米饭,或者只吃面条,或者只吃馒头(别管搭配),那么总共有3种方式。这就是加法原理的核心思想:如果完成一件事有几种不同的方法,且这些方法之间互不重叠,那么总的方法数就是各种方法数加起来。

加法原理就像你口袋里的零花钱——你有5元、2元、1元各一张,你想买一支3元的笔,你可以用5元(但找零麻烦),或者用2元+1元(正好),或者用1元+3次(但你没有3个1元)。这里每种付款方式都是不同的“方法”,并且互不重叠(一次付款只用一种方案),那么总方法数就是这些方案数加起来。当然,生活中我们可能不计较找零,但编程里我们常常用加法原理来统计“满足不同条件”的数据个数。


一、什么是加法原理?—— 一句话 + 一个生活例子

加法原理(也叫分类计数原理)是说:如果完成一件事有 nn 类互不重叠的方法,第一类有 m1m_1 种方法,第二类有 m2m_2 种方法,……,第 nn 类有 mnm_n 种方法,那么完成这件事共有 m1+m2++mnm_1 + m_2 + \cdots + m_n 种方法。

生活例子1:周末出去玩的选择
小明周末想出去玩,他可以选择去公园玩(有3个小项目:划船、碰碰车、摩天轮),或者去图书馆(有2种活动:看书、听讲座),或者去电影院(有4部电影可看)。注意,“去公园”“去图书馆”“去电影院”是三类完全不同的活动,不能同时进行(你不可能既在公园又同时在电影院)。所以小明周末有多少种不同的玩法?答案是 3+2+4=93 + 2 + 4 = 9 种。

生活例子2:课间零食选择
班上有三种零食:巧克力(5种口味)、饼干(3种口味)、果冻(2种口味)。你每节课间可以选一种零食吃(但一次只能吃一种),那么你一共有多少种选择?5+3+2=105+3+2=10 种。如果有一天你特别饿,想同时吃巧克力和饼干,那就不是加法原理了,那是组合问题。


二、加法原理在编程中的核心要求:分类互不重叠

使用加法原理时,最最重要的条件就是:各类之间没有重叠。如果一件事情可以同时属于两类,那么直接相加就会重复计算。比如你要统计“1到10中,是偶数或者大于5的数”。注意:比如6、8、10既是偶数又大于5,如果分别数偶数(2,4,6,8,10共5个)和大于5的数(6,7,8,9,10共5个),相加得到10,但实际只有2,4,6,7,8,9,10共7个(因为6,8,10被重复算了)。这就是重叠带来的错误。不过别担心,后面你学了“容斥原理”就能解决这类问题。但在我们目前的题目里,通常题目会保证类别互不干扰,比如“个位是3”和“个位是5”永远不会同时成立,所以可以直接加。

判断是否互斥的小技巧
问问自己:有没有一个数(或一个情况)可以同时属于两类?如果不可能,就放心相加。比如“性别是男”和“性别是女”互斥,“身高超过150cm”和“身高不超过150cm”互斥。但“年龄是10岁”和“身高是130cm”就不互斥(一个孩子可以同时满足)。


三、常见的加法原理编程场景

  1. 按不同位数的特征分类
    例如:统计1100中,个位是2或者百位是3的数。(注意:一个数同时满足两个条件不可能?实际可能,比如132个位是2且百位是1?不,百位是3的如300399,个位是2的如2,12,...,它们可能重叠吗?比如302?个位是2,百位是3,重叠!所以不能直接加,需要容斥。但如果是“个位是2”和“个位是3”,永远不会重叠,可以加。)

  2. 按不同的条件筛选
    例如:统计一个班级里,语文考满分的人数加上数学考满分的人数。如果没有人同时两科满分,就可以直接加;否则要减去同时满分的人数。

  3. 按不同类别统计
    例如:今天学校来了三种类型的家长:爸爸、妈妈、爷爷奶奶。分别统计数量后,总家长数就是三者之和(因为一个人不可能同时是爸爸和妈妈)。


四、新手最容易犯的错误

  • 忘记检查互斥性:直接看到两个条件就相加,结果重复计数。比如统计“能被2整除或者能被3整除的数”,如果直接数能被2的个数 + 能被3的个数,会把既能被2又能被3(即能被6整除)的数重复算两次。
  • 混淆加法原理和乘法原理:加法原理是“要么选A类,要么选B类”,乘法原理是“先选A类,再选B类”。比如“中午套餐有2种主食和3种饮料,一共有多少种搭配?”这是乘法(2×3=6)。而“今天休息可以选择去打篮球或者去游泳,打篮球有3种打法,游泳有2种泳姿”这是加法(3+2=5)。
  • 忽略边界条件:比如统计“1到50之间”是否包括1和50?计算循环时要确认起点和终点。

五、完整可运行的代码示例(带中文注释)

我们扩展一下原来的例子:统计1到100之间,个位是3、个位是5、或者个位是7的数各有多少个?因为这三个条件互斥(一个数的个位只能是一个数字),所以可以直接相加。

#include <iostream>
using namespace std;

int main() {
    int count3 = 0;  // 个位是3的个数
    int count5 = 0;  // 个位是5的个数
    int count7 = 0;  // 个位是7的个数
    
    for (int i = 1; i <= 100; i++) {
        if (i % 10 == 3) {
            count3++;  // 发现个位是3,计数器加1
        }
        if (i % 10 == 5) {
            count5++;  // 发现个位是5,计数器加1
        }
        if (i % 10 == 7) {
            count7++;  // 发现个位是7,计数器加1
        }
    }
    
    int total = count3 + count5 + count7;  // 加法原理:三类加起来
    cout << "个位是3的数有 " << count3 << " 个" << endl;
    cout << "个位是5的数有 " << count5 << " 个" << endl;
    cout << "个位是7的数有 " << count7 << " 个" << endl;
    cout << "总共有 " << total << " 个符合条件的数" << endl;
    return 0;
}

运行结果
个位是3的数有 10 个(3,13,23,...,93)
个位是5的数有 10 个(5,15,25,...,95)
个位是7的数有 10 个(7,17,27,...,97)
总共有 30 个符合条件的数

你可以验证:1~100中每10个数就有一个3、一个5、一个7,所以每个正好10个,加起来30个。完美!


六、更贴近校园生活的例子:统计考试成绩等级

假设一次考试,成绩满分100分,老师要统计“90分以上(含90)的同学人数”加上“60分以下(不含60)的同学人数”,因为一个人不可能同时属于这两个区间(互斥),所以可以直接相加。下面是程序片段:

int high = 0;  // 90分以上的人数
int low = 0;   // 60分以下的人数
int score;     // 某个同学的成绩

// 假设用循环输入成绩,这里用数组模拟
int scores[] = {95, 58, 72, 88, 45, 100, 61, 33, 92, 77};
int size = 10;  // 学生人数

for (int i = 0; i < size; i++) {
    if (scores[i] >= 90) {
        high++;  // 优秀
    } else if (scores[i] < 60) {
        low++;   // 不及格
    }
    // 注意:60~89之间的同学既不增加high也不增加low
}
int total = high + low;  // 加法原理
cout << "优秀人数:" << high << ",不及格人数:" << low << ",共" << total << "人" << endl;

七、相关知识点指引

  • 乘法原理:当你需要分步完成一件事(比如先选主食再选饮料)时,用乘法原理。它和加法原理一起构成计数问题的两大基石。
  • 容斥原理:当分类有重叠时,需要先加起来,再减去重复的部分。例如“1~100中能被2或3整除的数”就可以用容斥原理:50+3316=6750 + 33 - 16 = 67
  • 枚举法:加法原理经常和循环枚举配合使用,就像上面的代码一样,通过逐个数确认条件,然后累加计数器。

加法原理看似简单,但它是一个非常重要的逻辑工具。以后你遇到“满足某条件A或条件B的个数”时,先判断条件是否互斥,如果是,那就放心地“把不同的事情加起来”吧!

例题精讲

1单选题

小明有3本不同的数学书和2本不同的语文书。他计划从这些书中挑选一本数学书或者一本语文书带到学校,那么他一共有多少种不同的选择?

A5种
B6种
C3种
D2种
2单选题

从甲地到乙地可以乘飞机、火车或汽车。飞机有2个航班,火车有3趟车次,汽车有4趟班车。那么从甲地到乙地共有多少种不同的交通方式选择?

A24种
B9种
C12种
D7种
3判断题

在加法原理中,每一类方法必须能够独立完成整件事情,且各类方法之间互不重叠。

4填空题
以下C++代码用于计算从A地到B地的总路线数。已知从A到B可以经过C地或D地,从A到C有3条路,从A到D有4条路,从C到B有2条路,从D到B有3条路。注意:经过C和经过D是两种不同的方法,每种方法内部需要分步(乘法原理)。请补全代码完成总路线数计算。

int ways = 0;
int AC = 3, AD = 4, CB = 2, DB = 3;
ways = ___;
cout << ways;
5填空题
某商店有红色帽子3款,蓝色帽子2款,黑色帽子4款。小明要买一顶帽子,以下代码计算他有多少种选择。请补全横线处。

int red = 3, blue = 2, black = 4;
int total = ___;
cout << total;