这道题目出自蓝桥杯2013年省赛B组,属于典型的枚举类编程题。题目要求我们找到一个特定形式的表达式,即"带分数",其本质是将数字1到9进行排列组合,构造出满足特定条件的分数形式。
带分数的标准定义为:将一个数字N表示为整数部分加上一个真分数部分的形式,即 N = A + B/C。其中:
例如,当N=100时,一个合法的带分数表示是:100 = 3 + 69258/714。这里A=3,B=69258,C=714,所有数字1-9都被使用且不重复。
最直观的解法是枚举所有可能的排列组合:
虽然9!看起来很大,但对于现代计算机来说完全在可接受范围内。以C++为例,在OJ系统的时间限制下(通常1秒),这种规模的枚举是可行的。
直接的全排列枚举虽然可行,但仍有优化空间:
提前终止无效分支:在生成排列的过程中,如果前几位已经超过了N,可以立即终止当前分支的继续生成。
合理划分A的长度:整数部分A的位数通常不会太多。对于N=100这样的两位数,A的合理位数是1-2位。
分数部分的约束:
生成排列:使用标准库的next_permutation函数或自己实现的全排列算法。
分割数字:
cpp复制for(int aLen=1; aLen<=7; aLen++) { // A的位数
for(int bLen=1; bLen<=7-aLen; bLen++) { // B的位数
int cLen = 9 - aLen - bLen;
if(cLen <= 0) continue;
// 分割数字并转换为整数
int A = toNumber(perm, 0, aLen);
int B = toNumber(perm, aLen, aLen+bLen);
int C = toNumber(perm, aLen+bLen, 9);
// 检查条件
if(B >= C) continue;
if(A + B/C == N && B%C == 0) {
count++;
}
}
}
转换函数:将排列的一部分转换为整数
cpp复制int toNumber(int* arr, int start, int end) {
int num = 0;
for(int i=start; i<end; i++) {
num = num*10 + arr[i];
}
return num;
}
使用C++标准库的next_permutation是最简单高效的方式:
cpp复制int digits[] = {1,2,3,4,5,6,7,8,9};
do {
// 处理当前排列
} while(next_permutation(digits, digits+9));
注意:使用前需要确保数组是升序排列的,否则无法生成全部排列。
题目中的分数条件需要特别注意两点:
因此判断条件应该是:
cpp复制if(B % C == 0 && A + B/C == N) {
// 满足条件
}
提前计算N-A:可以将条件改写为B/C = N - A,先计算N-A,再检查B是否是C的倍数。
位数限制优化:
短路判断:在生成排列的过程中,如果前几位已经超过N,可以立即跳过。
cpp复制#include <iostream>
#include <algorithm>
using namespace std;
int toNumber(int digits[], int start, int end) {
int num = 0;
for(int i=start; i<end; i++) {
num = num*10 + digits[i];
}
return num;
}
int main() {
int N;
cin >> N;
int digits[] = {1,2,3,4,5,6,7,8,9};
int count = 0;
do {
for(int aLen=1; aLen<=7; aLen++) {
for(int bLen=1; bLen<=8-aLen; bLen++) {
int cLen = 9 - aLen - bLen;
if(cLen < 1) continue;
int A = toNumber(digits, 0, aLen);
int B = toNumber(digits, aLen, aLen+bLen);
int C = toNumber(digits, aLen+bLen, 9);
if(B % C == 0 && A + B/C == N) {
count++;
}
}
}
} while(next_permutation(digits, digits+9));
cout << count << endl;
return 0;
}
在实际OJ系统中,这样的复杂度是完全可接受的。
样例输入:
code复制100
预期输出:6(题目给出的样例)
边界测试:
特殊测试:
打印中间结果:在复杂排列问题中,打印关键变量有助于发现问题。
cpp复制if(A + B/C == N) {
cout << A << "+" << B << "/" << C << endl;
}
小规模测试:可以先测试数字1-3的排列,验证基本逻辑是否正确。
性能分析:对于最大输入,检查程序是否在合理时间内完成。
使用0-9的数字:如果允许使用0,需要考虑不能以0开头的约束。
其他运算符:如N = A × B / C 等形式。
不同数字范围:如只使用部分数字(如1-6)。
虽然枚举法在这里足够高效,但也可以考虑:
数学性质优化:分析N与A、B、C之间的数学关系,减少不必要的枚举。
并行计算:将排列生成和检查分配到多个线程。
记忆化搜索:缓存中间结果,避免重复计算。
这类排列组合问题在实际中有广泛应用,如:
解决此类问题的关键思路:
通过这道题目,我们不仅掌握了排列枚举的技巧,也学会了如何将数学表达式转化为程序逻辑。在实际编程中,这种将问题分解、逐步验证的思路非常重要。