加法与乘法原理的结合——解决复杂问题
较难2加法与乘法原理的“联合作战”——搞定复杂计数问题
生活中,我们经常要数一数有多少种选择:比如今天穿什么衣服、中午吃什么饭菜、出门带哪个包……有些问题很简单,要么是“可以选A或B”(加法原理),要么是“先选这个,再选那个”(乘法原理)。但更多问题其实把两种都混在一起了,比如:“你有两种书包和三种手提袋,每种袋子都要配一个文件袋,一共有多少种搭配方式?”这时候就需要两个原理联手解决。
下面我们就来学习如何拆解这类“混合问题”,让它们变得清晰好算。
1. 复习:加法原理和乘法原理的区别
-
加法原理:做一件事,有若干类不同的方法,每类方法里又有若干种具体方式。计算总数时,把每类的方式数加起来。关键词是“或者”(选择类)。
例:早饭你可以吃包子(3种馅)或者吃面包(2种口味),总共 3 + 2 = 5 种选择。 -
乘法原理:做一件事,需要连续分成几步,每一步又有若干种选择。总数等于每一步选择数的乘积。关键词是“并且”(分步类)。
例:先选上衣(3件),再选裤子(2条),总共 3 × 2 = 6 种搭配。
记住这个口诀:分类用加法,分步用乘法。
2. 复杂问题怎么拆?——“先分类,再分步”
当一个问题里既有“或者”又有“并且”时,秘诀是:先用加法原理把问题分成几个互不重叠的大类,然后针对每一类,再用乘法原理一步一步计算。最后把每一类的总数加起来。
这个思路就像分蛋糕:先把整个问题切成几块,每块蛋糕再切分着吃。
3. 经典案例:上学装备搭配
我们保留原来的例子,并深入分析。
你可以背书包(有红、蓝2种颜色),或者提手提袋(有黑、白、灰3种颜色)。每样还要选一个文件袋(有2种款式)。问一共有多少种搭配?
思考过程(保留原文)
- 首先,你要决定带书包还是手提袋,这是一个加法选择。书包有2种,手提袋有3种,不考虑文件袋时,选择袋子就有 2 + 3 = 5 种。
- 但是,不管选哪种袋子,你都要配一个文件袋(2种款式)。所以,对于每一种袋子选择,后续都有2种文件袋的选择。这是乘法原理:先选袋子,再选文件袋。
- 于是,对于书包类别:2种书包 * 2种文件袋 = 4种;对于手提袋类别:3种手提袋 * 2种文件袋 = 6种。
- 最后,把两类的结果用加法加起来:4 + 6 = 10 种。
图解分析(新增)
你可以想象画一棵树:
- 从树根出发,先分出两条大枝:书包、手提袋(加法分类)。
- 书包枝上再分出2根小枝(红、蓝),每根小枝又分出2根更小的枝(文件袋A、B),这样书包枝上一共 2×2=4 片叶子。
- 手提袋枝上分出3根小枝(黑、白、灰),每根小枝同样分2根文件袋枝,一共 3×2=6 片叶子。
- 所有叶子加起来就是 4+6=10 种搭配。
Python代码实现(保留原有并补充注释)
# 用列表存储不同类别的选项
backpacks = ["红书包", "蓝书包"] # 2种书包
bags = ["黑手提袋", "白手提袋", "灰手提袋"] # 3种手提袋
files = ["A款文件袋", "B款文件袋"] # 2种文件袋
# 对于书包类:2 * 2 = 4
backpack_combinations = len(backpacks) * len(files)
# 对于手提袋类:3 * 2 = 6
bag_combinations = len(bags) * len(files)
# 加法原理合并结果
total_combinations = backpack_combinations + bag_combinations
print("书包类搭配数:", backpack_combinations)
print("手提袋类搭配数:", bag_combinations)
print("总搭配数:", total_combinations)
# 输出:总搭配数:10
# 也可以直接列出所有搭配(先按加法分,再按乘法分步)
print("所有搭配如下:")
for bp in backpacks:
for f in files:
print(bp, "与", f)
for bg in bags:
for f in files:
print(bg, "与", f)
注意,在代码中我们用了两个独立的循环分别处理书包类和手提袋类,这正是“分类”思想的体现。
4. 另一个例子:用数字卡片组成两位数
用0、1、2三个数字组成两位数,十位不能是0。一共有多少个不同的两位数?
思考过程(保留原有)
我们可以拆成两种情况:十位是1时,个位有3种(0、1、2);十位是2时,个位也有3种。总数为 3 + 3 = 6。这就是加法思想(按十位分类),并且每一种情况里,十位和个位是乘法关系(1种 × 3种)。
# 数字卡片问题
tens_possible = [1, 2] # 十位不能为0
units_possible = [0, 1, 2] # 个位
# 用双重循环算出所有组合,并计数
count = 0
for ten in tens_possible:
for unit in units_possible:
print(ten * 10 + unit)
count += 1
print("一共能组成", count, "个两位数")
这里你发现了吗?我们其实先用了加法原理把十位分成1和2两类,然后对每一类,十位和个位是分步关系(先选十位,再选个位),所以用乘法。只不过因为每一类里十位只有1种,所以乘法结果就是个位的种类数。
5. 更多生活中的例子
例子1:食堂选餐
学校食堂提供两种套餐选择:
- 套餐A:从3种主食(米饭、馒头、面条)中选1种,再从2种荤菜(鸡腿、排骨)中选1种,再从2种素菜(青菜、土豆丝)中选1种。
- 套餐B:从2种炒饭(蛋炒饭、扬州炒饭)中选1种,再送一碗汤(2种汤可选)。
问:小明想吃一份完整的套餐(要么选A,要么选B),一共有多少种搭配?
分析:
- 这是一个典型的“先分类,再分步”问题。
- 第一类(套餐A):3种主食 × 2种荤菜 × 2种素菜 = 12种。
- 第二类(套餐B):2种炒饭 × 2种汤 = 4种。
- 总搭配数 = 12 + 4 = 16种。
# 食堂选餐
# 套餐A
staple = ["米饭", "馒头", "面条"] # 3种主食
meat = ["鸡腿", "排骨"] # 2种荤菜
veggie = ["青菜", "土豆丝"] # 2种素菜
# 套餐B
fried_rice = ["蛋炒饭", "扬州炒饭"] # 2种炒饭
soup = ["紫菜汤", "番茄汤"] # 2种汤
# 分类计算
planA = len(staple) * len(meat) * len(veggie) # 3*2*2=12
planB = len(fried_rice) * len(soup) # 2*2=4
total = planA + planB
print("套餐A搭配数:", planA)
print("套餐B搭配数:", planB)
print("总搭配数:", total) # 输出16
例子2:游戏角色装备
在你的游戏里,主角可以穿铠甲(有3种颜色)并佩戴一把武器(有2种),或者穿魔法袍(有2种颜色)并佩戴一个护身符(有4种)。另外,不管穿哪类装备,都要选择一双鞋(有3种)。问有多少种角色搭配?
分析:先分类(铠甲类 或 魔法袍类),每一类里再分步。
- 铠甲类:3种铠甲 × 2种武器 × 3种鞋子 = 18
- 魔法袍类:2种魔法袍 × 4种护身符 × 3种鞋子 = 24
- 总数 = 18 + 24 = 42
# 游戏角色装备
armor = ["红铠", "蓝铠", "金铠"] # 3种铠甲
weapon = ["剑", "弓"] # 2种武器
robe = ["紫袍", "绿袍"] # 2种魔法袍
amulet = ["火符", "水符", "风符", "雷符"] # 4种护身符
shoes = ["皮靴", "布鞋", "铁靴"] # 3种鞋子(公共步骤)
# 分类计算
armor_class = len(armor) * len(weapon) * len(shoes) # 18
robe_class = len(robe) * len(amulet) * len(shoes) # 24
total = armor_class + robe_class
print("铠甲类搭配:", armor_class)
print("魔法袍类搭配:", robe_class)
print("总搭配:", total) # 42
6. 新手容易犯的错误
错误1:不分类,直接全部相乘或相加
有的同学看到题目里既有“或者”又有“并且”,就胡乱把数字乘在一起或加在一起,导致结果错误。
例如上学装备的例子,有人会错误地计算成:2 × 3 × 2 = 12(这是错误的,因为书包和手提袋不能同时选)。正确做法是先分类,再对每一类相乘。
错误2:分类时遗漏情况或重复计数
比如用数字组成两位数,有人可能把十位为0也算进去,导致多算。或者把十位是1和十位是2的两类重复计算同一数字(比如11在两类里都出现?不会,因为每类只有一个十位)。所以分类必须互斥且完备。
错误3:混淆“分步”的顺序
在乘法原理中,每一步的选择数可能相互独立,但有时顺序会带来不同结果。例如先选衣服再选裤子,和先选裤子再选衣服,结果一样。但如果步骤之间有条件限制(比如某上衣只能配某裤子),就要小心了。好在我们这类计数问题通常默认选项独立。
错误代码示例(纠正)
假设有人错误地把上学装备的代码写成:
# 错误做法:把所有选项乘在一起
wrong_total = len(backpacks) * len(bags) * len(files)
print("错误结果:", wrong_total) # 2*3*2=12,实际应为10
原因是书包和手提袋是“或者”关系,不是“并且”关系。正确的做法必须用加法分开。
7. 完整可运行的综合示例
下面是一个完整的例子,包含了从题目到代码的全过程。
题目:
学校举办“才艺展示”活动,每名学生可以报名参加歌唱类或舞蹈类。
- 歌唱类:先选一首歌曲(有4首),再选择一个伴奏形式(原唱、伴奏2种)。
- 舞蹈类:先选一支舞蹈(有3支),再选择演出服装(有2套),最后还要选一个道具(有2种道具)。
请问一共有多少种不同的展示方案?
分析:先分类(歌唱或舞蹈),再对每类分步。
歌唱类:4 × 2 = 8
舞蹈类:3 × 2 × 2 = 12
总方案数:8 + 12 = 20
# 才艺展示方案计数
# 歌唱类选项
songs = ["歌A", "歌B", "歌C", "歌D"] # 4首歌曲
accompany = ["原唱", "伴奏"] # 2种伴奏
# 舞蹈类选项
dances = ["舞1", "舞2", "舞3"] # 3支舞蹈
costumes = ["服装1", "服装2"] # 2套服装
props = ["道具A", "道具B"] # 2种道具
# 分类计算
sing_count = len(songs) * len(accompany) # 4*2=8
dance_count = len(dances) * len(costumes) * len(props) # 3*2*2=12
total = sing_count + dance_count
print("歌唱类方案数:", sing_count)
print("舞蹈类方案数:", dance_count)
print("总共方案数:", total) # 输出20
# 如果想列出所有方案,可以这样:
print("歌唱类方案:")
for s in songs:
for a in accompany:
print(" 唱", s, ",", a, "版")
print("舞蹈类方案:")
for d in dances:
for c in costumes:
for p in props:
print(" 跳", d, ",穿", c, ",拿", p)
运行这段代码,你会看到每个方案的文字描述,有助于理解“分类”和“分步”的直观含义。
8. 相关指引
学会了加法原理和乘法原理的结合使用,你就掌握了计数问题的核心思想。接下来可以学习:
- 排列:从n个不同元素中取出m个,按顺序排成一列,有多少种方法?
- 组合:从n个不同元素中取出m个,不考虑顺序,有多少种方法?
- 树状图:用画图的方式直观展示所有情况,特别适合分类分步问题。
这些知识会在概率统计、编程算法(如枚举、动态规划)中经常用到。记住,遇到复杂计数题,先深呼吸,问自己:这是“或者”还是“并且”? 然后用“先分类,再分步”的方法,一步一步拆解,再复杂的题目也能迎刃而解。
例题精讲
一个三位数的密码锁,每位数字从0到9。规则:第一位不能是0,且如果第一位是奇数,则后两位必须都是偶数;如果第一位是偶数,则后两位必须都是奇数。那么这样的密码有多少个?
在使用加法原理和乘法原理解决计数问题时,如果一个问题可以分成多个互斥的情况,每个情况内部又可以分成若干个连续的步骤,那么可以先对每个情况用乘法原理计算,再将所有情况的结果用加法原理求和。
以下函数计算所有由5位数字组成的密码(每位0-9),要求第一位是奇数,最后一位是偶数,并且中间三位不能全是奇数。请填写空缺的表达式。
def count_passwords():
# 第一位奇数:5种;最后一位偶数:5种;中间三位任意:10^3种
# 减去中间三位全是奇数的情况:5^3种
return 5 * 5 * (___)小明要从A地到C地,中间必须经过B地。从A到B有3种交通方式:飞机、火车、轮船;从B到C有2种交通方式:汽车、轮船。但是,如果A到B选择飞机,则B到C不能选择轮船(因为时间冲突)。问小明从A到C共有多少种不同的行程方案?
以下函数计算所有由四个数字组成的四位数(千位不能是0),要求该数中至少有两个数字相同。请填写空缺的表达式。
def count_numbers():
# 所有四位数个数:千位1-9共9种,其余三位0-9各10种
total = 9 * 10 * 10 * 10
# 所有数字都不相同的四位数个数:千位9种,百位9种(不能等于千位),十位8种,个位7种
no_repeat = 9 * 9 * 8 * 7
return total - ___