bala
从一开始的一头雾水,完全不懂如何下手。接着认真再看了紫书、博客,还是不知道怎么下手。期间尝试过跟着别人的代码敲,但,完全不懂他们的思路。
本着实践出真知的精神,于是我!!!决定从一个小规模的、比原题简单的问题入手,搞定一个小规模问题,那说不定就能弄懂原题了,照葫芦画瓢嘛。
于是我纠结的问题就缩小成:输入真分数 a / b,求加数个数为3的情况下的埃及分数解,即把 b / a 表示成 1 / m + 1 / n + 1 / p
balabala
从早上九点到晚上八点,终于有点突破咯,感觉快要解决咯,哈哈,有点小激动,记录一下。
——2020/4/18 20:30
宁看,
在茫茫可选答案中找到了正确答案,哈哈哈哈哈,这波,这波是
——2020/4/18 20:40
思路
理一下我的头绪:
- 采用递归算法(应该算深度优先搜索DFS)
- 对递归深度进行枚举
eg. 深度maxd = 3,代表用3个加数来表示 a / b; - 另外有个重要的函数是估价函数,即判断在什么情况下不可能在当前的深度限制中得出解。
实现:剩下的分数若 > (下一层最大的分数 乘 剩下的深度),则 直接返回上一层即可,不可能得出解了。
注: 其实要是真的不理解,可以试试解决问题:输入真分数 a / b,求加数个数为3的情况下的埃及分数解,即把 b / a 表示成 1 / m + 1 / n + 1 / p;
真的是可以从这个小问题入手,举一反三,最后解决这个问题的!
最终代码
客官若要复制我的代码看的话,建议把一些注释删了,注释部分是我调试过程中留下的,hh,虽然可能没人看,但是,万一有呢?
//对于整个问题,一头雾水
// 方向________________>>>>>>>>>
//那就先弄懂小规模问题: a / b 分解 成三个埃及分数。列出所有情况。
//2020/4/18
//9:00 - 21:30
//@reasonbao
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long int ll;
//比较 y1 / x1 与 y2 / x2 的大小
int CompareFraction(ll y1, ll x1, ll y2, ll x2) {
ll fraction_front = y1 * x2, fraction_next = y2 * x1; //通分交叉相乘,乘积可能非常大
if (fraction_front == fraction_next) return 0;
else if (fraction_front > fraction_next) return 1;
else return 2;
}
//估价函数
bool Assess(ll fenzi, ll fenmu, ll layer, ll biggest) {
if (CompareFraction(fenzi, fenmu, layer, biggest) == 2 || CompareFraction(fenzi, fenmu, layer, biggest) == 0)
return true; //"假想最大值",至少应该要比剩余的分数大或等于
return false; //若剩余层数 * 最大埃及分数 < 剩余的分数," 剪枝 "
}
//最大公约数,用于约分
ll Gcd(ll a, ll b) {
if (b == 0) return a;
else return Gcd(b, a % b);
}
//约分
void Reduce(ll &fenzi, ll &fenmu) {
if (fenzi == 0) {
fenzi = 0;
fenmu = 1;
}
else {
ll gcd = Gcd(fenzi, fenmu);
fenzi /= gcd;
fenmu /= gcd;
}
}
void Sub(ll &y1, ll &x1, ll y2, ll x2) {
ll fraction_front = y1 * x2, fraction_next = y2 * x1; //通分交叉相乘,乘积可能非常大
x1 = x1 * x2;
y1 = fraction_front - fraction_next;
Reduce(y1, x1);
}
ll ans[1000];
ll ans_best[1000];
bool g_is_ok = false;
//void Dfs(ll remain_fenzi, ll remain_fenmu, ll from, ll cur, ll maxd) {
//
// for (ll i = from; ; i ++ ) { //向第cur层填入埃及分数,。 注:from初始化为1,即 1 / 1
//
// while (CompareFraction(remain_fenzi, remain_fenmu, 1, i) == 2) {
cout << "减之前:"; cout << remain_fenzi << "/" << remain_fenmu << " - " << 1 << "/" << i << endl;
cout << "标记1111\n";
// i ++ ; //直到找到该层可填的数 (条件:1 / i 比剩余分数 小 或者 等)
// }
//
// ans[cur] = i;
// cout << "减之前:"; cout << remain_fenzi << "/" << remain_fenmu << " - " << 1 << "/" << i << endl;
// Sub(remain_fenzi, remain_fenmu, 1, i);
// cout << "减之后:"; cout << remain_fenzi << "/" << remain_fenmu << endl;
//
// if (remain_fenzi == 0) {
// cout << "数组情况:————";
// for (int m = 0; m < 10; m ++ ) cout << ans[m] << " "; cout << endl << endl;
// }
//
// Dfs(remain_fenzi, remain_fenmu, i + 1, cur + 1, maxd);
// cout << "标记222\n";
// }
// cout << "标记333\n";
//}
//void Dfs(ll remain_fenzi, ll remain_fenmu, ll from, ll cur, ll maxd) {
//
// if (cur == maxd ) {
// if ( ! remain_fenzi) {
// cout << "数组情况:————";
// for (int m = 0; m < 10; m ++ ) cout << ans[m] << " "; cout << endl << endl;
// }
// return;
// }
//
// for (ll i = from; ; i ++ ) { //向第cur层填入埃及分数,。 注:from初始化为1,即 1 / 1
//
// if (! Assess(remain_fenzi, remain_fenmu, maxd - cur, i)) return; //不可能事件,返回上一层 (上一层尝试其他数),不知道这算不算 ""剪枝""
//
// while (CompareFraction(remain_fenzi, remain_fenmu, 1, i) == 2) {
// i ++ ; //直到找到该层可填的数 (条件:1 / i 比剩余分数 小 或者 等)
// }
//
// ans[cur] = i;
// cout << "减之前:"; cout << remain_fenzi << "/" << remain_fenmu << " - " << 1 << "/" << i << endl;
//
// ll remain_fenzi_tmp = remain_fenzi;
// ll remain_fenmu_tmp = remain_fenmu;
//
// Sub(remain_fenzi_tmp, remain_fenmu_tmp, 1, i);
//
// cout << "减之后:"; cout << remain_fenzi_tmp << "/" << remain_fenmu_tmp << endl;
//
// Dfs(remain_fenzi_tmp, remain_fenmu_tmp, i + 1, cur + 1, maxd);
// }
//}
//比较相同深度情况下的更优解
void Better(ll pos) {
if ( !ans_best[pos] || ans[pos] <= ans_best[pos]) {
for (int i = 0; i <= pos; i ++ )
ans_best[i] = ans[i];
}
}
void Dfs(ll remain_fenzi, ll remain_fenmu, ll from, ll cur, ll maxd) {
if (cur == maxd ) {
if ( ! remain_fenzi) {
cout << "数组情况:————";
for (int m = 0; m < maxd; m ++ ) cout << ans[m] << " "; cout << endl << endl;
Better(maxd - 1);
g_is_ok = true;
}
return;
}
for (ll i = from; ; i ++ ) { //向第cur层填入埃及分数,。 注:from初始化为1,即 1 / 1
if (! Assess(remain_fenzi, remain_fenmu, maxd - cur, i)) return; //不可能事件,返回上一层 (上一层尝试其他数),不知道这算不算 ""剪枝""
while (CompareFraction(remain_fenzi, remain_fenmu, 1, i) == 2) {
i ++ ; //直到找到该层可填的数 (条件:1 / i 比剩余分数 小 或者 等)
// cout << "(((\n";
}
ans[cur] = i;
// cout << "减之前:"; cout << remain_fenzi << "/" << remain_fenmu << " - " << 1 << "/" << i << endl;
ll remain_fenzi_tmp = remain_fenzi;
ll remain_fenmu_tmp = remain_fenmu;
Sub(remain_fenzi_tmp, remain_fenmu_tmp, 1, i);
// cout << "减之后:"; cout << remain_fenzi_tmp << "/" << remain_fenmu_tmp << endl;
Dfs(remain_fenzi_tmp, remain_fenmu_tmp, i + 1, cur + 1, maxd);
}
}
void OutputResult(ll a, ll b, ll maxd) {
printf("%lld/%lld=", a, b);
for (int i = 0; i < maxd; i ++ ) {
if (i >= 1) printf("+");
printf("%lld/%lld", 1, ans_best[i]);
}
cout << endl;
}
int main() {
// ll a,b,c,d;
// while (cin >> a >> b >> c >> d) {
cout << CompareFraction(a,b,c,d) << endl;
//
cout << Assess(a,b,c,d);
//
cout << Gcd(a,b);
//
Reduce(a, b); Reduce(c, d);
cout << a << " " << b << " " << c << " " << d << endl;
//
//
// }
// memset(ans, 0, sizeof(ans));
// memset(ans_best, 0, sizeof(ans_best));
// Dfs(495, 499, 1, 0, 6);
// cout << "RESULT: ";
// for (int m = 0; m < 6; m ++ ) cout << ans_best[m] << " "; cout << endl << endl;
ll a,b;
while (cin >> a >> b) {
for (ll maxd = 1; ; maxd ++ ) { //对DFS的深度进行枚举,也就是限制深度(实现题目中:加数少的情况比多的好)
memset(ans, 0, sizeof(ans));
memset(ans_best, 0, sizeof(ans_best));
Dfs(a, b, 1, 0, maxd);
if (g_is_ok) {
OutputResult(a, b, maxd);
break;
}
}
g_is_ok = false; //记得重置
}
return 0;
}
这代码,我看出了紫书中 递归枚举排列 的影子,哈哈,果然还是有收获的,啃这本书!
END
临渊羡鱼,不如退而结网
——————————————
若有什么错误,欢迎大佬指正!
若有问题,也欢迎在评论提出!
——————————————