您的位置:首页 > 其它

埃及分数问题(迭代加深搜+剪枝)!

2016-06-27 19:43 141 查看
题意:给你个真分数,你需要将其化简为最少的若干特殊真分数之和,你要输出这个序列(序列按递增序)。如果有不同的方案,则分数个数相同的情况下使最大的分母最小。若还相同,则使次大的分母最大……以此类推。如:2/3=1/2+1/6,但不允许2/3=1/3+1/3,因为加数中有相同的。对于一个分数a/b,表示方法有很多种,但是哪种最好呢? 首先,加数少的比加数多的好,其次,加数个数相同的,最小的分数越大越好。

这道题不同于其他搜索的的主要是它的搜索没有明显的上界和下界,无法直接使用BFS和DFS,例如这道题中设a/b=1/x1+1/x2+1/x3+.......1/xn对于生成的解答数根本无法知道它的深度(即n值)和宽度(对于此题为(1,+∞)),所以我们选择用迭代加深搜;

迭代加深搜索,实质上是限定下界的深度优先搜索。即首先允许深度优先搜索K层,若没有发现可行解,再将K+1后

重复以上步骤搜索,直到搜索到可行解。

迭代加深搜还有这些好处:

1.空间开销小   每个深度下实际上是一个深度优先搜索,不过深度有限制,而DFS的空间消耗小是众所周知的。

2.利于深度剪枝

3.时间效率不低
虽然重复搜索,但是大家不难理解,前一次搜索跟后一次相不是微不足到的。

从这里来看迭代加深搜索算法就是仿广度优先搜索的深度优先搜索。既能满足深度优先搜索的线性存储要求,又能保证发现一个最小深度的目标结点。

从实际应用来看,迭代加深搜索的效果比较好,并不比广度优先搜索慢很多,但是空间复杂度却与深度优先搜索相同,比广度优先搜索小很多。

使用搜索算法的时候,选择正确的搜索方式很重要。当有一类问题需要做广度优先搜索,但却没有足够的空间,而时间却很充裕,碰到这类问题,我们可以选择迭代加深搜索算法。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cassert>
using namespace std;
int a, b, maxd;

long long gcd(long long a, long long b) {
return b == 0 ? a : gcd(b, a%b);
}
// 返回满足1/c <= a/b的最小c
inline int get_first(long long a, long long b) {
return b/a+1;
}
const int maxn = 100 + 5;
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];
}
return false;
}
// 当前深度为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 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; }
}
cout << "Case " << ++kase << ": ";
if(ok) {
cout << a << "/" << b << "=";
for(int i = 0; i < maxd; i++) cout << "1/" << ans[i] << "+";
cout << "1/" << ans[maxd] << "\n";
} else cout << "No solution.\n";
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: