B3836 [GESP202303 二级] 百鸡问题
一、题目理解
1.1 题目背景
这是一道典型的“百钱买百鸡”类问题。题目中有三种物品,它们的单价分别是 x、y、z,现在要用 n 元钱买 m 件物品,问有多少种购买方案。
1.2 变量含义
根据代码中的变量名,我们可以推断出各变量的含义:
-
x:第一种物品的单价
-
y:第二种物品的单价
-
z:第三种物品的单价
-
n:总钱数
-
m:总件数
注意: 第三种物品的单价是 z,但代码中写的是 k/z,也就是说第三种物品是“z 元钱买 1 件”还是“1 元钱买 z 件”需要根据实际情况判断。从代码逻辑看,i*x + j*y + k/z == n 表示总花费等于 n,所以第三种物品的单价是 z(即每件 z 元),且要求 k % z == 0,说明 k 必须是 z 的倍数。
等等,这里需要仔细分析: 如果第三种物品单价是 z,那么 k 件的花费应该是 k*z,而不是 k/z。所以更合理的解释是:第三种物品是“z 件一组,每组 1 元”,即单价是 1/z 元/件。这种设定在“百钱买百鸡”问题中很常见(比如小鸡 1 元 3 只)。
1.3 题目要求
输入 x、y、z、n、m,输出满足以下条件的方案数:
-
第一种物品买 i 件,第二种物品买 j 件,第三种物品买 k 件。
-
总件数:i + j + k = m
-
总花费:i × x + j × y + k ÷ z = n(且 k 能被 z 整除)
-
i、j、k 都是非负整数
输出: 一个整数,表示满足条件的方案数。
1.4 样例模拟
假设输入为:x=3,y=2,z=1,n=10,m=8。
意思是:第一种物品 3 元/件,第二种物品 2 元/件,第三种物品 1 元/件(z=1 表示 1 件 1 元),总钱数 10 元,总件数 8 件。
我们需要找到所有满足条件的 (i, j, k) 组合。
二、核心思想:双重枚举 + 计算第三个数
2.1 什么是双重枚举?
想象有三种物品,我们不知道每种买多少件。但有一个关键关系:
总件数 = 第一种件数 + 第二种件数 + 第三种件数
也就是说,如果我们确定了第一种和第二种物品的数量,第三种物品的数量就自动确定了:
第三种件数 = 总件数 - 第一种件数 - 第二种件数
所以只需要枚举第一种和第二种物品的数量,第三种直接算出来即可,不需要三重循环。
2.2 为什么这种方法可行?
因为三种物品的数量之和是固定的(等于 m)。知道其中两个,第三个就唯一确定了。这样把三重循环降为双重循环,效率大大提高。
2.3 枚举范围的确定
-
第一种物品最多买 n/x 件(因为总钱数是 n,单价是 x,不可能超过 n/x 件)。
-
第二种物品最多买 n/y 件。
-
第三种物品的数量由 m - i - j 算出,需要保证它非负,且能被 z 整除。
三、程序执行流程逐步解析
下面按照程序实际执行的顺序,逐步讲解每一步在做什么。所有代码片段在文末统一给出,这里只讲逻辑。
【第 1 步】定义全局变量
在程序开头定义六个全局变量:x、y、z、n、m、sum。
-
x、y、z:三种物品的单价(或第三种物品的“z 件 1 元”中的 z)
-
n:总钱数
-
m:总件数
-
sum:方案计数器,初始值为 0
为什么用全局变量?
全局变量默认初始化为 0,所以 sum 不需要手动赋初值。另外,全局变量在程序的任何地方都可以访问,方便在循环和判断中使用。
【第 2 步】程序开始
程序从主函数开始执行。
【第 3 步】读入数据
从输入流中依次读取 x、y、z、n、m 五个整数。
【第 4 步】外层循环:枚举第一种物品的数量
循环变量 i 从 0 开始,每次加 1,直到 i 大于 n/x 时停止。
-
i = 0:第一种物品买 0 件
-
i = 1:第一种物品买 1 件
-
……
-
i = n/x:第一种物品最多买 n/x 件(因为总钱数是 n,单价是 x)
为什么上限是 n/x?
因为第一种物品的总花费是 i × x,不可能超过总钱数 n。所以 i × x ≤ n,即 i ≤ n/x。
【第 5 步】内层循环:枚举第二种物品的数量
循环变量 j 从 0 开始,每次加 1,直到 j 大于 n/y 时停止。
-
j = 0:第二种物品买 0 件
-
j = 1:第二种物品买 1 件
-
……
-
j = n/y:第二种物品最多买 n/y 件
为什么上限是 n/y?
同理,第二种物品的总花费是 j × y,不可能超过总钱数 n。
【第 6 步】计算第三种物品的数量
对于每一组 (i, j),第三种物品的数量 k 由总件数关系算出:
k = m - i - j
为什么?
因为总件数 = 第一种件数 + 第二种件数 + 第三种件数,即 m = i + j + k,所以 k = m - i - j。
【第 7 步】验证条件
对于每一组 (i, j, k),检查以下条件是否全部满足:
条件一:k 必须非负
k ≥ 0,即 m - i - j ≥ 0。如果 k 是负数,说明前两种物品的数量之和已经超过总件数 m,不可能。
条件二:总花费等于 n
i × x + j × y + k ÷ z = n
注意: 这里第三种物品的花费是 k/z,而不是 k×z。这说明第三种物品是“z 件 1 元”的设定(类似百钱买百鸡中的小鸡 1 元 3 只)。所以 k 必须是 z 的倍数,否则花费不是整数。
条件三:k 能被 z 整除
k % z == 0
因为第三种物品是“z 件一组”,必须整组购买,不能买半组。所以 k 必须是 z 的倍数。
三个条件同时满足,才说明这是一组有效的方案。
【第 8 步】方案计数
如果三个条件都满足,就把方案计数器 sum 加 1。
【第 9 步】循环结束
当外层和内层循环都结束后,sum 就保存了所有满足条件的方案数。
【第 10 步】输出结果
输出 sum 的值,并换行。
#include<bits/stdc++.h> // 万能头文件 using namespace std; int x, y, z, n, m, sum; // 全局变量:三种单价、总钱数、总件数、方案计数(默认0) int main(){ cin >> x >> y >> z >> n >> m; // 读入五种数据 for(int i = 0; i <= n/x; i++){ // 枚举第一种物品的数量,上限 n/x for(int j = 0; j <= n/y; j++){// 枚举第二种物品的数量,上限 n/y int k = m - i - j; // 第三种物品的数量 = 总件数 - 前两种数量 if(k >= 0 && i*x + j*y + k/z == n && k % z == 0){ // 三个条件同时满足 sum++; // 方案数加一 } } } cout << sum << endl; // 输出方案总数 return 0; }

浙公网安备 33010602011771号