《P1763 埃及分数》
题目描述
来源:BIO 1997 Round 1 Question 3
在古埃及,人们使用单位分数的和(形如 a1 的,a 是自然数)表示一切有理数。如:32=21+61,但不允许 32=31+31,因为加数中有相同的。对于一个分数 ba,表示方法有很多种,但是哪种最好呢?首先,加数少的比加数多的好,其次,加数个数相同的,最小的分数越大越好。如:
45194519451945194519=31+121+1801=31+151+451=31+181+301=41+61+1801=51+61+181
最好的是最后一种,因为 181 比 1801,451,301 都大。
注意,可能有多个最优解。如:
2115921159=41+361+6331+37981=61+91+6331+37981
由于方法一与方法二中,最小的分数相同,因此二者均是最优解。
给出 a,b,编程计算最好的表达方式。保证最优解满足:最小的分数 ≥1071。
输入格式
一行两个整数,分别为 a 和 b 的值。
输出格式
输出若干个数,自小到大排列,依次是单位分数的分母。
输入输出样例
输入 #1复制
19 45
输出 #1复制
5 6 18
说明/提示
1<a<b<1000
代码实现:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long LL;
vector<LL> ans_d, temp_d;
int max_depth;
// 求最大公约数
LL gcd(LL a, LL b) {
return b == 0 ? a : gcd(b, a % b);
}
// 约分分数 a/b
pair<LL, LL> reduce(LL a, LL b) {
LL g = gcd(a, b);
return {a / g, b / g};
}
// 比较两个解的优劣
bool better(const vector<LL>& a, const vector<LL>& b) {
if (a.empty()) return false;
if (b.empty()) return true;
for (int i = a.size() - 1; i >= 0; i--) {
if (a[i] != b[i]) return a[i] < b[i];
}
return false;
}
// 深度优先搜索寻找最优解
bool dfs(int depth, LL a, LL b, LL from) {
if (depth == max_depth) {
if (a != 1) return false;
if (b < from) return false;
temp_d.push_back(b);
if (better(temp_d, ans_d)) {
ans_d = temp_d;
}
temp_d.pop_back();
return true;
}
bool found = false;
from = max(from, (b + a - 1) / a); // 计算k的下界
for (LL k = from; ; k++) {
// 剪枝:如果剩余的深度无法达到更好的解
LL max_remaining = b * (max_depth - depth + 1);
if (k > max_remaining) break;
LL new_a = a * k - b;
LL new_b = b * k;
if (new_a <= 0) continue;
pair<LL, LL> reduced = reduce(new_a, new_b);
LL na = reduced.first;
LL nb = reduced.second;
temp_d.push_back(k);
if (dfs(depth + 1, na, nb, k + 1)) {
found = true;
}
temp_d.pop_back();
}
return found;
}
int main() {
LL a, b;
cin >> a >> b;
pair<LL, LL> reduced = reduce(a, b);
a = reduced.first;
b = reduced.second;
for (max_depth = 1; ; max_depth++) {
temp_d.clear();
if (dfs(1, a, b, (b + a - 1) / a)) {
break;
}
}
sort(ans_d.begin(), ans_d.end());
for (vector<LL>::iterator it = ans_d.begin(); it != ans_d.end(); ++it) {
cout << *it << " ";
}
cout << endl;
return 0;
}