埃及分数问题(迭代加深搜+剪枝)!
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.时间效率不低
虽然重复搜索,但是大家不难理解,前一次搜索跟后一次相不是微不足到的。
从这里来看迭代加深搜索算法就是仿广度优先搜索的深度优先搜索。既能满足深度优先搜索的线性存储要求,又能保证发现一个最小深度的目标结点。
从实际应用来看,迭代加深搜索的效果比较好,并不比广度优先搜索慢很多,但是空间复杂度却与深度优先搜索相同,比广度优先搜索小很多。
使用搜索算法的时候,选择正确的搜索方式很重要。当有一类问题需要做广度优先搜索,但却没有足够的空间,而时间却很充裕,碰到这类问题,我们可以选择迭代加深搜索算法。
这道题不同于其他搜索的的主要是它的搜索没有明显的上界和下界,无法直接使用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; }
相关文章推荐
- 必选项:你的网站易于阅读吗?
- Ubuntu 16.04 字体变小的恢复方法
- IXIA线下配置流量
- 2016.6.25【初中部 NOIP提高组 】模拟赛C
- oracle字符查出一位
- log4j.properties log4j.xml 路径问题
- Spring 与hibernate整合过程中出现的一些问题
- 使用C#开发HTTP服务器系列之实现Get和Post
- 五.旋转
- JAVA虚拟机(JVM)和垃圾收集机制是什么
- Linux中miscdevice的分析
- Android 切换横竖屏问题
- Handler 实现轮询的效果(自己对自己发送消息)
- c语言堆和栈的小问题和程序在vc6和GCC下遇到的不同区别
- Android 自定义控件(一)
- 如何扩大ImageView的点击区域
- 魔兽世界编年史
- 运行Appium碰到的坑们
- DFT basics
- NSDate使用