mirror of https://github.com/OI-wiki/OI-wiki
fix(search/idastar): 埃及分数部分样例卡死 (#5364)
Co-authored-by: Tifa <62847935+Tiphereth-A@users.noreply.github.com>pull/5445/head
parent
49f7db3f82
commit
2541772ece
|
@ -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";
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -1 +1,2 @@
|
|||
495 499
|
||||
495 499
|
||||
3 997
|
Loading…
Reference in New Issue