由“埃及分数”引入迭代加深搜索
来源:互联网 发布:java简单售票系统 编辑:程序博客网 时间:2024/06/05 22:44
迭代加深搜索(IDDFS)的思想
迭代加深搜索一般用来求解状态树“非常深”,甚至深度可能趋于无穷,但是“目标状态浅”的问题。如果用普通的DFS去求解,往往效率不够高。此时我们可以对DFS进行一些改进。最直观的一种办法是增加一个搜索的最大深度限制dep,一般是从1开始。每次搜索都要在dep深度之内进行,如果没有找到解,就继续增大dep,直到成功找到解,然后break。
如下图所示,如果用DFS,需要15步才能找到结点3,但是用迭代加深搜索,很快即可找到结点3。
为了更好的理解该算法,下面以“埃及分数”这一经典的例子来作为说明。
埃及分数问题
在古埃及,人们使用单位分数的和(即1/a,a是自然数)来表示一切有理数,
例如,2/3=1/2+1/6,但是不允许2/3=1/3+1/3,因为在加数中不允许有相同的。
对于一个分数a/b,表示的方法有很多种,其中加数少的比加数多的好,如果加数个数相同,那么最小的分数越大越好。
例如,19/45=1/5+1/6+1/18是最优方案。
输入两个整数a,b(0<a<b<1000),试编程计算最佳表达式。
样例输入:
19 45
样例输出:
5 6 18
问题分析
本题由于搜索层数不明,所以考虑迭代加深搜索。
确定了搜索模式后,易得到以下两个基本思路:
1.枚举对象:分母。因为a/b = 1/a[1] + 1/a[2] + 1/a[3] + ...... + 1/a[n],所以不妨设a[1] < a[2] < a[3] < ..... < a[n]。
2.剪枝手段:定分母上下界。
设限定搜索层数为dep,当前搜索到第k层,当前正要枚举分母a[k],还需枚举总和为x/y的分数。
answer[i]表示当前最优解中的第i个分母,如果还没有解则表示正无穷。 则,
下界:max(y/x,a[k-1]) +1 <= a[k]
[两种情况(下界越大越好):
1.因为必然a[k-1] < a[k],所以a[k-1] + 1 <= a[k]。
2.如果x/y正好等于现要求的分数(如1/18),则y/x便是分母(a[k]是分母)。]
上界:a[k] <= min(y/x * (dep-k+1),anwser[dep]-1,maxlongint/x)
[三种情况(上界越小越好):
1.(平分)因为剩余层数为(dep-k+1),还需枚举总和为x/y的分数,
则可列出式子 (dep-k+1) * 1/a[k] = x/y,求得a[k]最大 = y/x * (dep-k+1)。
2.当有解时(作标记),a[k] <= anwser[dep]-1。
3.最大范围确定一个解,maxlongint/x,防止溢出。]
代码
#include <bits/stdc++.h>using namespace std;const long long INF = 0x7fffffff / 2;bool flag; //标记是否有解 long long d[20], answer[20];long long gcd(long long a, long long b) { if(b == 0) return a; return gcd(b, a % b);}void dfs(long long x, long long y, int k) { //决定第k个分母d[k]的值,x分子,y分母 if(k == dep + 1) return; else if(x == 1 && y > d[k - 1]) { //此时x/y不必再分解,单独作为其中一个解 d[k] = y; if(!flag || d[k] < answer[k]) //为有解或当前最优解较优 memcpy(answer, d, sizeof(d)); flag = 1; //标记已经有解(可行解) return ; } else { long long s, t, m; s = max(y / x, d[k - 1]) + 1; //确定d[k]的上下界,s,t t = (dep - k + 1) * y / x; if(t > INF) t = INF; //防止溢出 if(flag && t > d[dep] - 1) t = d[dep] - 1; //上界为已求最优解 - 1 for(long long i = s; i <= t; i ++) { d[k] = i; m = gcd(x * i - y, y * i); // a/b - 1/i = x * i - y / y * i dfs((x * i - y) / m, (y * i) / m), k + 1); //继续搜索 } }}int main() { long long A, B; scanf("%lld%lld", &A, &B); long long k = gcd(A, B); A/=k; B/=k; flag = 0; d[0] = 1; for(int dep = 1; dep < 10; dep ++) { //枚举得到最大深度 dfs(A, B, 1); if(flag) { for(int i = 1; i <= dep; i ++) printf("%d ", answer[i]); break; } }}
- 由“埃及分数”引入迭代加深搜索
- 埃及分数 迭代加深搜索 IDS
- 迭代加深搜索 埃及分数
- 埃及分数,迭代加深搜索
- 埃及分数(迭代加深搜索)
- 埃及分数(迭代加深搜索)
- 埃及分数题解[迭代加深搜索]
- 埃及分数(迭代加深搜索)
- 迭代加深搜索(埃及分数)
- 迭代加深搜索与埃及分数
- 埃及分数问题(迭代加深搜索)
- 埃及分数 迭代加深搜索 IDA*
- 埃及分数(迭代加深搜索)
- 迭代加深搜索--埃及分数
- [codevs1288]埃及分数 迭代加深搜索
- codevs1288埃及分数-迭代加深搜索
- 埃及分数-迭代加深
- 迭代加深搜索与埃及分数求解
- 337. House Robber III
- Redis单点测试用,安装部署
- 第一次使用java连接mongodb遇到的问题
- java 代码 规范 快捷键
- Go实现tls的通信的简单代码例子
- 由“埃及分数”引入迭代加深搜索
- 剑指offer-面试题9-菲波那切数列
- Redis---String数据类型的操作
- ubuntu 与windows双系统记录
- codeforce B. Bash's Big Day
- LightOJ 1236 Pairs Forming LCM(唯一分解定理)
- iOS手势与点击事件冲突
- 【WPF】给下拉列表ComboBox绑定数据
- 【Unity问题】为什么Unity 5.5.0f3(64-bit)免费版打开项目每次要输入账号密码