zoukankan      html  css  js  c++  java
  • 【迭代加深搜索】埃及分数问题

    谢谢阿苏~http://blog.csdn.net/urecvbnkuhbh_54245df/article/details/5856756

    【迭代加深搜索(ID,iterative deepening)】:从小到大枚举上限maxd,每次执行只考虑深度不超过maxd的结点。

      ------对于可以用回溯法求解但解答树的深度没有明显上限的题目,可以考虑ID算法;

      ------优点:它主要是在递归搜索函数的开头判断当前搜索的深度是否大于预定义的最大搜索深度,如果大于,就退出这一层的搜索,如果不大于,就继续进行搜索。这样最终获得的解必然是最优解。它避免了广度优先搜索占用搜索空间太大的缺点,也减少了深度优先搜索的盲目性。

    【A*算法(启发式搜索)】:A*(A-Star)算法是一种静态路网中求解最短路最有效的直接搜索方法。估价值与实际值越接近,估价函数取得就越好。

      A*算法的估价函数可表示为:f'(n) = g'(n) + h'(n)

      这里,f'(n)是估价函数,g'(n)是起点到节点n的最短路径值,h'(n) 是n到目标的最短路经的启发值。由于这个f'(n)其实是无法预先知道的,所以我们用前面的估价函数f(n)做近似。g(n)代替g'(n),但 g(n)>=g'(n)才可(大多数情况下都是满足的,可以不用考虑),h(n)代替h'(n),但h(n)<=h'(n)才可(这一点特别的重要)。可以证明应用这样的估价函数是可以找到最短路径的,也就是可采纳的。我们说应用这种估价函数的最好优先算法就是A*算法。举一个例子,其实广度优先算法就是A*算法的特例。其中g(n)是节点所在的层数,h(n)=0,这种h(n)肯定小于h'(n),所以由前述可知广度优先算法是一种可采纳的。实际也是。当然它是一种最臭的A*算法。

    【IDA*算法】:综合了A*算法的人工智能性和回溯法对空间的消耗较少的优点,在一些规模很大的搜索问题中会起意想不到的效果。它的具体名称是 Iterative Deepening A*, 1985年由Korf提出。该算法的最初目的是为了利用深度搜索的优势解决广度A*的空间问题,其代价是会产生重复搜索。归纳一下,IDA*的基本思路是:首先将初始状态结点的H值设为阈值maxH,然后进行深度优先搜索,搜索过程中忽略所有H值大于maxH的结点;如果没有找到解,则加大阈值maxH,再重复上述搜索,直到找到一个解。在保证H值的计算满足A*算法的要求下,可以证明找到的这个解一定是最优解。在程序实现上,IDA* 要比 A* 方便,因为不需要保存结点,不需要判重复,也不需要根据 H值对结点排序,占用空间小。
    而这里在IDA*算法中也使用合适的估价函数,来评估与目标状态的距离。

    在一般的问题中是这样使用IDA*算法的,当前局面的估价函数值+当前的搜索深度 > 预定义的最大搜索深度时,就进行剪枝。

    这个估计函数的选取没有统一的标准,找到合适的该函数并不容易,但是可以大致按照这个原则:在一定范围内加大各个状态启发函数值的差别。

    e.g.经典埃及分数问题:

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

    输入整数a, b,编程计算最佳表达式。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstdlib>
     4 #include<cstring>
     5 #include<set>
     6 using namespace std;
     7 typedef long long LL;
     8 const int maxn = 100010;
     9 LL ans[maxn], v[maxn], maxd;//枚举深度上限
    10 LL get_first(LL a, LL b)
    11 {
    12     return b/a+1;
    13 }
    14 LL gcd(LL a, LL b)
    15 {
    16     return b == 0 ? a : gcd(b, a%b);
    17 }
    18 bool better(LL d)
    19 {
    20     for(LL i = d; i >= 0; i--)
    21     {
    22         if(v[i] != ans[i])
    23         {
    24             return (ans[i] == -1 || v[i] < ans[i]);
    25         }
    26     }
    27     return false;
    28 }
    29 bool dfs(LL d, LL c, LL a, LL b)
    30 {
    31     //cout << a << " " << b << endl;
    32     if(d == maxd)
    33     {
    34         if(b%a) return false; //若到最大深度时最后一个分数不能化为埃及分数。即分子为1的情况
    35         v[d] = b/a; //深度为d时的最优埃及分数是 1/(b/a);
    36         if(better(d))
    37         {
    38             memcpy(ans, v, sizeof(LL)*(d+1));
    39         }
    40         return true;
    41     }
    42     bool ok = false;
    43     c = max(c, get_first(a, b));
    44     for(LL i = c; ; i++)
    45     {
    46         if((maxd-d+1) * b <= a * i) break;
    47         v[d] = i;
    48         LL a2 = a*i-b, b2 = b*i;
    49         LL g = gcd(a2, b2); //约分
    50         if(dfs(d+1, i+1, a2/g, b2/g)) ok = true;
    51     }
    52     return ok;
    53 }
    54 int main()
    55 {
    56     int T;
    57     scanf("%d", &T);
    58     for(int kase = 1; kase <= T; kase++)
    59     {
    60         LL a, b;
    61         scanf("%lld%lld", &a, &b);
    62 
    63         /*main code*/
    64         for(maxd = 1; ; maxd++) //枚举深度上限
    65         {
    66             memset(ans, -1, sizeof(ans));
    67             memset(v, -1, sizeof(v));
    68             LL c = get_first(a, b);
    69             if(dfs(0, c, a, b))
    70             {
    71                 break;
    72             }
    73         }
    74         /*-------------------------------------*/
    75         printf("Case %d: %lld/%lld=", kase, a, b);
    76         for(int i = 0; i <= maxd; i++)
    77         {
    78             if(i) printf("+");
    79             printf("1/%lld", ans[i]);
    80         }
    81         printf("
    ");
    82 
    83     }
    84     return 0;
    85 }
    View Code
  • 相关阅读:
    [Swift]LeetCode795. 区间子数组个数 | Number of Subarrays with Bounded Maximum
    [Swift]LeetCode794. 有效的井字游戏 | Valid Tic-Tac-Toe State
    [Swift]LeetCode793. 阶乘函数后K个零 | Preimage Size of Factorial Zeroes Function
    [Swift]LeetCode792. 匹配子序列的单词数 | Number of Matching Subsequences
    [Swift]LeetCode1012. 至少有 1 位重复的数字 | Numbers With 1 Repeated Digit
    [Swift]LeetCode1011. 在 D 天内送达包裹的能力 | Capacity To Ship Packages Within D Days
    [Swift]LeetCode1010. 总持续时间可被 60 整除的歌曲 | Pairs of Songs With Total Durations Divisible by 60
    [Swift]LeetCode1009. 十进制整数的补码 | Complement of Base 10 Integer
    [Swift]LeetCode791. 自定义字符串排序 | Custom Sort String
    转 OGG add trandata 到底做了什么
  • 原文地址:https://www.cnblogs.com/LLGemini/p/4319261.html
Copyright © 2011-2022 走看看