fix(search/idastar): 埃及分数部分样例卡死 (#5364)

Co-authored-by: Tifa <62847935+Tiphereth-A@users.noreply.github.com>
pull/5445/head
Lavender Tree 2024-03-10 16:16:41 +08:00 committed by GitHub
parent 49f7db3f82
commit 2541772ece
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 51 additions and 54 deletions

View File

@ -1,71 +1,66 @@
// 埃及分数问题
#include <algorithm>
#include <cassert>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
int a, b, maxd;
const int MAX_E = 1e7;
int a, b;
vector<int> ans;
vector<int> current;
long long gcd(long long a, long long b) { return b == 0 ? a : gcd(b, a % b); }
inline bool better() { return ans.empty() || current.back() < ans.back(); }
// 返回满足 1/c <= a/b 的最小 c 值
int get_first(long long a, long long b) { return b / a + 1; }
bool dfs(int d, long a, long b, int e) {
if (d == 0) {
if (a == 0 && better()) ans = current;
return a == 0;
}
const int maxn = 100 + 5;
long _gcd = gcd(a, b);
a /= _gcd;
b /= _gcd;
long long v[maxn], ans[maxn];
// 如果当前解 v 比目前最优解 ans 更优,更新 ans
bool better(int d) {
for (int i = d; i >= 0; i--)
if (v[i] != ans[i]) {
return ans[i] == -1 || v[i] < ans[i];
bool solved = false;
// the min value of e:
// a/b - 1/e >= 0
// e >= b/a
int e1 = max(e + 1, int((b + a - 1) / a));
// b/a <= e <= MAX_E
// b/a <= MAX_E
if (b > a * MAX_E) {
return false;
}
for (;; e1++) {
// the max value of e:
// d * (1/e) >= a/b
// d/e >= a/b
if (d * b < a * e1) {
return solved;
}
return false;
current.push_back(e1);
// a/b - 1/e
solved |= dfs(d - 1, a * e1 - b, b * e1, e1);
current.pop_back();
}
return solved;
}
// 当前深度为 d分母不能小于 from分数之和恰好为 aa/bb
bool dfs(int d, int from, long long aa, long long bb) {
if (d == maxd) {
if (bb % aa) return false; // aa/bb 必须是埃及分数
v[d] = bb / aa;
if (better(d)) memcpy(ans, v, sizeof(long long) * (d + 1));
return true;
}
bool ok = false;
from = max(from, get_first(aa, bb)); // 枚举的起点
for (int i = from;; i++) {
// 剪枝:如果剩下的 maxd+1-d 个分数全部都是 1/i加起来仍然不超过
// aa/bb则无解
if (bb * (maxd + 1 - d) <= i * aa) break;
v[d] = i;
// 计算 aa/bb - 1/i设结果为 a2/b2
long long b2 = bb * i;
long long a2 = aa * i - bb;
long long g = gcd(a2, b2); // 以便约分
if (dfs(d + 1, i + 1, a2 / g, b2 / g)) ok = true;
}
return ok;
int solve() {
ans.clear();
current.clear();
for (int maxd = 1; maxd <= 100; maxd++)
if (dfs(maxd, a, b, 1)) return maxd;
return -1;
}
int main() {
int kase = 0;
while (cin >> a >> b) {
int ok = 0;
for (maxd = 1; maxd <= 100; maxd++) { // 枚举深度上限
memset(ans, -1, sizeof(ans));
if (dfs(0, get_first(a, b), a, b)) { // 开始进行搜索
ok = 1;
break;
}
}
int maxd = solve();
cout << "Case " << ++kase << ": ";
if (ok) {
if (maxd > 0) {
cout << a << "/" << b << "=";
for (int i = 0; i < maxd; i++) cout << "1/" << ans[i] << "+";
cout << "1/" << ans[maxd] << "\n";
for (int i = 0; i < maxd - 1; i++) cout << "1/" << ans[i] << "+";
cout << "1/" << ans[maxd - 1] << "\n";
} else
cout << "No solution.\n";
}

View File

@ -1 +1,2 @@
Case 1: 495/499=1/2+1/5+1/6+1/8+1/3992+1/14970
Case 1: 495/499=1/2+1/3+1/8+1/30+1/3992+1/14970
Case 2: 3/997=1/345+1/14955+1/22931

View File

@ -1 +1,2 @@
495 499
495 499
3 997