题意:把a/b写成不同的埃及分数之和,要求项数尽量小,在此前提下最小的分数尽量大,然后第二小的分数尽量大……另外有k (0≤k≤5) 个数不能用作分母。例如,k=0时 5/121=1/33+1/121+1/363 ,不能使用33是最优解为 5/121=1/45+1/55+1/1089 。输入保证 2≤a<b≤876,gcd(a,b)=1 ,且会挑选比较容易求解的数据。(本段摘自《算法竞赛入门经典(第2版)》)
分析:
IDA*思想。在原来的埃及分数上加一些限制条件即可。
代码:
#include <iostream>
#include <algorithm>
#include <fstream>
#include <cstring>
#include <vector>
#include <queue>
#include <cmath>
#include <cctype>
#include <stack>
#include <set>
using namespace std;
const int maxn = 1000 + 5, INF = 1e8;
int T, k;
long long a, b;
long long x[10], add[maxn], ans[maxn];
long long gcd(long long x, long long y)
{
return (y == 0) ? x : gcd(y, x % y);
}
long long get_first(long long x, long long y)
{
return y / x + 1;
}
bool better(int deep)
{
for (int i = deep; i >= 0; --i)
if (ans[i] != add[i])
return (ans[i] == -1 || add[i] < ans[i]);
return false;
}
bool DFS(long long aa, long long bb, long long from, int deep, int limit)
{
if (deep == limit)
{
if (bb % aa)
return false;
add[deep] = bb / aa;
for (int i = 0; i < k; ++i)
if (add[deep] == x[i])
return false;
if (better(deep))
memcpy(ans, add, sizeof(long long) * (deep + 1));
return true;
}
bool ok = false;
for (long long i = max(get_first(aa, bb), from); ; ++i)
{
if (bb * (limit - deep + 1) <= i * aa)
break;
bool flag = true;
for (int j = 0; j < k; ++j)
if (i == x[j])
{
flag = false;
break;
}
if (flag)
{
long long tmpa = aa * i - bb, tmpb = i * bb;
long long tmp = gcd(tmpa, tmpb);
tmpa /= tmp;
tmpb /= tmp;
add[deep] = i;
if (DFS(tmpa, tmpb, i + 1, deep + 1, limit))
ok = true;
}
}
return ok;
}
int main()
{
scanf("%d", &T);
for (int C = 0; C < T; ++C)
{
memset(ans, -1, sizeof(ans));
scanf("%lld%lld%d", &a, &b, &k);
for (int i = 0; i < k; ++i)
scanf("%d", &x[i]);
printf("Case %d: ", C + 1);
for (int maxd = 0; ; ++maxd)
if (DFS(a, b, get_first(a, b), 0, maxd))
{
printf("%lld/%lld=", a, b);
for (int i = 0; i <= maxd; ++i)
if (i < maxd)
printf("1/%lld+", ans[i]);
else
printf("1/%lld\n", ans[i]);
break;
}
}
return 0;
}