大家好,我是game1024,一名喜欢代码的测试同学,最近在做一些基础的编程题练手,希望通过将解题的过程记录下来,可以帮助自己熟悉一下c++语言各种操作
问题描述
小M面对一组从 1 到 9 的数字,这些数字被分成多个小组,并从每个小组中选择一个数字组成一个新的数。目标是使得这个新数的各位数字之和为偶数。任务是计算出有多少种不同的分组和选择方法可以达到这一目标。
numbers
: 一个由多个整数字符串组成的列表,每个字符串可以视为一个数字组。小M需要从每个数字组中选择一个数字。
例如对于[123, 456, 789]
,14个符合条件的数为:147 149 158 167 169 248 257 259 268 347 349 358 367 369
。
测试样例
样例1:
输入:
numbers = [123, 456, 789]
输出:14
样例2:
输入:
numbers = [123456789]
输出:4
样例3:
输入:
numbers = [14329, 7568]
输出:10
解题思路
整体思路
-
理解题目要求:
- 需要从每个数字组中选择一个数字,使得这些数字的各位数字之和为偶数。
- 每个数字组是一个字符串,表示一组数字。
- 比如:有三组数
[123, 456, 789]
,从123里面选择1,从456里面选择4,从789里面选择7,组成新的数字是147, 1+4+7 = 12,是偶数,所以这是其中一种方案,总计所有满足需求的方案总数
-
分析数字的奇偶性:
- 一个数字的各位数字之和为偶数的条件是:所有选出的数字的各位数字之和为偶数。
- 奇数 + 奇数 = 偶数
- 偶数 + 偶数 = 偶数
- 奇数 + 偶数 = 奇数
-
统计每个数字组中的奇数和偶数个数:
- 对于每个数字组,统计其中奇数和偶数的个数。
-
递归计算组合数:
- 使用递归的方式,先从一个数字组中选择数字,
- 如果选择是奇数,那么再
从剩余组里面选出和为奇数
的方案,就能满足条件 - 如果选择是偶数,那么再
从剩余组里面选出和为偶数
的方案,就能满足条件 - 因为每次递归,问题规模都会下降,直到递归至最小规模时,问题变成求一组数字中的偶数个数,所以,该递归算法有效
- 如果选择是奇数,那么再
- 使用递归的方式,先从一个数字组中选择数字,
-
返回最终结果:
- 最终结果是所有组合中各位数字之和为偶数的组合数。
1.分析一组数字中的奇偶个数
因为题目给的一组数字是int类型,所以我们需要先实现一个算法,遍历一个int类型当中,每一位的数字奇偶,方法就是用取余操作符%
- 比如当一个数字
(n % 10)
的时候,结果得到的就是个位上的数字 - 然后利用
n=n/10
缩减10倍,原来十位上的数字就变成了个位,继续(n % 10)
就得到十位上的数字 - 重复这个过程,我们就能扫描一个int类型当中,每一位的数字的奇数和偶数的数量了
为了方便,我这里定义的count
函数返回两个值,一个是奇数的数量,一个是偶数的数量
- count(number)[0] 为奇数的数量
- count(number)[1] 为偶数的数量
// 判断是奇数
bool isJI(int num) { return num % 2; }
// 遍历int类型的每一位(按十进制遍历),返回其中的奇数 偶数的个数
tuple<int, int> count(int number) {
int JI = 0, OU = 0;
for (int i = 10; number > 0; number /= 10) {
if (isJI(number % 10)) {
JI++;
} else {
OU++;
}
}
return std::make_tuple(JI, OU);
}
2.递归计算组合数
关于递归算法思路,先用张图简单释义一下数字的奇偶性
伪代码如下
// 先对两个函数定义
f(numbers, n) = 第n组(从0开始)开始选择,sum结果为奇数的方案数量
g(numbers, n) = 第n组(从0开始)开始选择,sum结果为偶数的方案数量
// 奇数 = 奇数 + 偶数 or 偶数 + 奇数
f(numbers, n) = 第n组中的奇数数量 * g(numbers, n+1) + 第n组中的偶数数量 * f(numbers, n+1)=
// 偶数 = 奇数 + 奇数 or 偶数 + 偶数
g(numbers, n) = 第n组中的偶数数量 * g(numbers, n+1) + 第n组中的奇数数量 * f(numbers, n+1)
这个函数的终止条件是 n = numbers.size() - 1
cpp代码,可以看到我并没有定义f(n),g(n)两个函数,我是直接使用std::tuple
返回两个值,
- solutionJiOu(numbers, start)[0] 是奇数的数量数量
- solutionJiOu(numbers, start)[1] 是偶数的方案数量
tuple<int, int> solutionJiOu(const std::vector<int> &numbers, int start) {
if (start == numbers.size() - 1) {
return count(numbers[start]);
}
int Ji, Ou;
std::tie(Ji, Ou) = count(numbers[start]);
tuple<int, int> result = solutionJiOu(numbers, start + 1);
int otherJi, otherOu;
std::tie(otherJi, otherOu) = result;
// 奇数 = 奇数 + 偶数 or 偶数 + 奇数
int JiPossible = Ji * otherOu + Ou * otherJi;
// 偶数 = 奇数 + 奇数 or 偶数 + 偶数
int OuPossible = Ji * otherJi + Ou * otherOu;
return std::make_pair(JiPossible, OuPossible);
}
3.返回最终结果
利用std::tie
对元组进行解包,将偶数方案数量返回即可
int solution(std::vector<int> numbers) {
// Please write your code here
int Ji, Ou;
std::tie(Ji, Ou) = solutionJiOu(numbers, 0);
return Ou;
}
完整代码
递归实现
#include <iostream>
#include <tuple>
#include <vector>
using std::tuple;
// 判断是奇数
bool isJI(int num) { return num % 2; }
// 统计一个数字中,奇数 偶数的个数
tuple<int, int> count(int number) {
int JI = 0, OU = 0;
for (int i = 10; number > 0; number /= 10) {
if (isJI(number % 10)) {
JI++;
} else {
OU++;
}
}
return std::make_tuple(JI, OU);
}
tuple<int, int> solutionJiOu(const std::vector<int> &numbers, int start) {
if (start == numbers.size() - 1) {
return count(numbers[start]);
}
int Ji, Ou;
std::tie(Ji, Ou) = count(numbers[start]);
tuple<int, int> result = solutionJiOu(numbers, start + 1);
int otherJi, otherOu;
std::tie(otherJi, otherOu) = result;
// 奇数 = 奇数 + 偶数 or 偶数 + 奇数
int JiPossible = Ji * otherOu + Ou * otherJi;
// 偶数 = 奇数 + 奇数 or 偶数 + 偶数
int OuPossible = Ji * otherJi + Ou * otherOu;
return std::make_pair(JiPossible, OuPossible);
}
int solution(std::vector<int> numbers) {
// Please write your code here
int Ji, Ou;
std::tie(Ji, Ou) = solutionJiOu(numbers, 0);
return Ou;
}
int main() {
// You can add more test cases here
std::cout << (solution({123, 456, 789}) == 14) << std::endl;
std::cout << (solution({123456789}) == 4) << std::endl;
std::cout << (solution({14329, 7568}) == 10) << std::endl;
return 0;
}
涉及的知识点有
- 遍历数字的每一位:利用(n%10)遍历数字的每一位
- 函数返回两个值
- 元组:
std::tuple
- 解包:
std::tie
- 元组:
- 递归思想:当一个问题的规模可以不断下降时,就能使用