迭代加深搜索

首先这个不要怕这个东西,其实我刚开始学这个搜索也觉得特别高大上,觉得都是很高大上让人听不懂的专业术语,其实说白了迭代加深搜索的思想和精髓就是控制了搜索深度的dfs,但是却能够达到广搜的效果。它的搜索状态无穷多种,深度可以是无限深,宽度也可以是无限宽。当你采用深搜时会超时,当你采用广搜时队列会螺旋爆炸,这时就需要上迭代加深。

比如有这样一类题让你在这类题里面找到最小的解就可以。(通常情况下迭代加深搜索的题都是有解,只是解的大小问题),怎么样写这个呢?其实就是深搜控制了深度,搜索历程就是先搜索深度为1的所有情况,看看能不能达到最终目标,如果能达到最终目标,结束搜索。如果不能继续增加可以搜索的深度,但是前面所有的搜索的结果不会被保存,下一次深度加1继续从头开始搜虽然看起来好像干了很多重复的事情,其实不是的,当你搜索第k的深度的时候,前面k-1深度的所有情况都不值得一提。这也就是为什么迭代加深不会像广搜一样超出内存,如果超了内存你来打我~~并且时间上并不比广搜慢太多。

应用场景

1.当你拿到题发现时间很充足,并且内存可能无法满足你搜索时拓展出的n多状态。

2.题目一般情况下有解并且可能有多个解让你求出最优的解。

3.想用就用吧~~

经典例题

迭代加深入门第一题------埃及分数
埃及分数,没有题目连接但是是一个很经典的迭代加深搜索的例子,题目要求就是给定一个分数让你把这个分数拆成若干个分数相加的和,并且这些分数的分子都是1。分数个数尽量少,而且最小的分数尽量大,也就是最小的那个分数的分母越小越好,典型的迭代加深搜索的例子。
最后就是注意剪枝很重要,ai >= b(maxd-d+1)的含义:a/b >= (maxd-d+1)/i;为啥这样判断?因为我们知道分数的分母是越来越大的,在当前这个深度分母最小都是i,后面分母越来越大,不可能都是i,所以,如果假设当前深度之后的每一步分母都是i,也需要maxd-d+1个相同的分子,很显然这样的情况不存在。因为分母越来越大,分数越来越小就算你后面所有分数的极限值都是1/i,全部加起来也不过是(maxd-d+1)/i,如果当前分数剩余的分数值都大于等于这个放缩的极限值那么这个深度肯定没有解。

//注意换long long,乘积的时候防止爆炸
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
#define ll long long
ll s[100],v[100];
ll gcd(ll a,ll b)           //gcd约分缩小运算数
{while(b){ll t = a%b;a = b;b = t;}return a;
}
bool better(int d)          //与之前的比较结果做比较
{for(int i = d;i >= 0;i--){if(s[i] != v[i]){if(s[i] == -1 || s[i] > v[i]){       //判断当前的v是不是比之前的s更优return true;  //从后面判断更直观,因为前面很多分母都是相同的,后面往前判断更快}else{return false;}}}return false;
}
bool iddfs(int d,int maxd,ll from,ll a,ll b)
{if(d == maxd){           //达到最大深度if(b%a){            //最后一个分数a/b不能约分成分子为1的情况return false;}v[d] = b/a;if(better(d)){     memcpy(s,v,sizeof(ll)*(d+1));          //拷贝d+1个分数分母,等价于下面的循环//        for(int i = 0;i <= d;i++){//         s[i] = v[i];//     }}return true;}from = max(from,b/a+1);                        //取大一点的开始运算bool flag = false;for(int i = from;;i++){            if(a*i >= b*(maxd-d+1)){           //  a/b >= (maxd-d+1)/i;break;             //很重要的一个剪枝技巧用了数学的放缩法}v[d] = i;ll na = a*i - b;                    //新的分子 ll nb = b*i;                        //新的分母 ll g = gcd(na,nb);                  //最大公约数,尽量约分,以便缩小a,b的范围,防止超出if(iddfs(d+1,maxd,i+1,na/g,nb/g)){flag = true;}}return flag;
}
int main()
{freopen("C:\\Users\\Administrator\\Desktop\\input.txt","r",stdin);freopen("C:\\Users\\Administrator\\Desktop\\output2.txt","w",stdout);int a,b;while(cin>>a>>b){memset(v,0,sizeof(v));int d;for(d = 1;d <= 100;d++){                    //最大深度100已经很大。memset(s,-1,sizeof(s));if(iddfs(0,d,b/a+1,a,b)){break;}}if(d > 100){                  //无解,我拍了400组数据没有一组无解。。。。printf("No solution.\n");}else{printf("%d/%d = ",a,b);for(int i = 0;i < d;i++){printf("1/%lld+",s[i]);}printf("1/%lld.\n",s[d]);}}return 0;
}

迭代加深入门第二题------骑士精神

题目大意就是给你一个5*5的棋盘问你最多15步之内能不能抵达给定的状态。如果能够抵达那么最小的步数是多少,如果不能抵达输出-1。很明显的迭代加深搜索题,搭配估计函数做一点预期估计。如果上来直接不控制深度,肯定超时,毕竟dfs那么笨他哪知道什么时候停下来转弯不是?所以我们用迭代加深控制它,得出来的结果即是最优跑起来又最快,题目可以去VJ上找。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
int goal[5][5] = {{1,1,1,1,1},{0,1,1,1,1},{0,0,2,1,1},{0,0,0,0,1},{0,0,0,0,0}};
int map[5][5];
int dx[] = {2,2,-2,-2,1,1,-1,-1};
int dy[] = {1,-1,1,-1,2,-2,2,-2};
int h()             //计算与预期棋盘不相同棋子的个数
{int cnt = 0;for(int i = 0;i < 5;i++){for(int j = 0;j < 5;j++){if(map[i][j] != goal[i][j]){cnt++;}}}return cnt;
}
bool iddfs(int d,int maxd,int x,int y)
{if(h() == 0){                //如果直接达到了最终状态直接返回return true;}if(h() + d - 1 > maxd){       //进行估计return false;     //当前搜索的次数与估价的次数和是否能够在达到最大深度之前得出答案}for(int i = 0;i < 8;i++){int nx = x + dx[i];int ny = y + dy[i];if(nx < 0 || nx > 4 || ny < 0 || ny > 4){continue;}swap(map[x][y],map[nx][ny]);if(iddfs(d+1,maxd,nx,ny)){return true;}swap(map[x][y],map[nx][ny]);} return false;
}
int main()
{int t;scanf("%d",&t);while(t--){int sx,sy,d;for(int i = 0;i < 5;i++){char s[10];scanf("%s",s);for(int j = 0;j < 5;j++){if(s[j] == '1' || s[j] == '0'){map[i][j] = s[j] - '0';}if(s[j] == '*'){map[i][j] = 2;sx = i;sy = j;}}}for(d = 1;d <= 15;d++){if(iddfs(0,d,sx,sy)){break;}}if(d > 15){printf("-1\n");}else{printf("%d\n",d);}}return 0;
}

迭代加深入门第三题------次方问题

POJ3134
就是给一个数n,问从x^1开始怎么 样最快算到x^n,可以利用中间的计算结果进行乘除运算,实则就是上面的指数进行加减运算。所以问题就缩减为1------n怎么样通过利用中间运算获得的答案进行加法或者减法运算快速得到n。并且要求输出最小的运算次数,很显然无论怎么样都有解,只是解的大小问题,最大次数没有上限,最小次数却又下限。采用迭代加深思想深度越小先搜到的肯定是最小答案。

乐观估计:当前剩余拓展的深度次数maxd-d的差值假设是m,意味着还可以拓展m次,就算m次运算都是针对当前的值进行翻倍运算,最大也就是使当前这个数x翻了2^m,如果这样都比n小那么没有必要在算下去,这个深度下该x算不到答案。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <cmath>
using namespace std;
int n,s[1005];
bool iddfs(int d,int maxd,int x)
{if(d > maxd || (x*(1<<(maxd-d)) < n)){     //超出最大深度和乐观估计return false;      }if(x == n){                      //拓展到节点结束搜索return true;}s[d] = x;for(int i = 0;i <= d;i++){         //拓展所有的情况if(iddfs(d+1,maxd,x+s[i])){      //所有的加法情况return true;}if(iddfs(d+1,maxd,fabs(x-s[i]))){return true;    //所有的减法情况,记得加绝对值不允许出现指数是负数的情况}}return false;         //搜不到答案
}
int main()
{while(~scanf("%d",&n) && n){int i;for(i = 0; true ;i++){memset(s,0,sizeof(s));if(iddfs(0,i,1)){break;}}printf("%d\n",i);}return 0;
}

迭代加深入门第四题------旋转游戏
题目传送门HDU1667
题目大意就是ABCDEFGH八种操作,每次只能进行一种操作,问最小多少步能够使得中间的8块同为1或者2或者3。迭代加深搜索思想 + 剪枝技巧

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cmath>
using namespace std;
int s[8][7] = {{0,2,6,11,15,20,22},            //A{1,3,8,12,17,21,23},         //B{10,9,8,7,6,5,4},                //C{19,18,17,16,15,14,13},          //D{23,21,17,12,8,3,1},         //E{22,20,15,11,6,2,0},         //F{13,14,15,16,17,18,19},          //G{4,5,6,7,8,9,10},                //H
};
int center[] = {6,7,8,11,12,15,16,17};         //中心8个位置的坐标
int ans[] = {5,4,7,6,1,0,3,2,-1};              //反向回溯对应的操作
int map[25];                                    //数字图
char way[105];                                  //操作方法
int get_h()                             //乐观估计
{       //找出中间8个位置1,2,3最多的那一个的数目int cnt[4] = {0};int num = -1;for(int i = 0;i < 8;i++){cnt[map[center[i]]]++;num = max(num,cnt[map[center[i]]]);}return 8 - num;
}
int get_move(int k)             //移动
{int t = map[s[k][0]];for(int i = 0;i < 6;i++){map[s[k][i]] = map[s[k][i+1]];}map[s[k][6]] = t;
}
bool iddfs(int d,int maxd,int k)
{if(d > maxd || get_h() + d > maxd){         //超出最大允许深度或者估计之后无解return false;}if(get_h() == 0){                 //到达目标状态way[d] = '\0';printf("%s\n%d\n",way,map[6]);return true;}for(int i = 0;i < 8;i++){           //八种情况拓展if(ans[i] != k){               //当前搜索和上一次搜索不互逆,小优化~way[d] = 'A' + i;get_move(i);                //正向移动if(iddfs(d+1,maxd,i)){return true;}get_move(ans[i]);         //反向移回}}return false;
}
int main()
{int n;while(~scanf("%d",&n) && n){map[0] = n;for(int i = 1;i < 24;i++){scanf("%d",&map[i]);}if(get_h() == 0){             //给的状态直接上目标状态printf("No moves needed\n%d\n",map[6]);continue;}for(int i = 1; true ;i++){if(iddfs(0,i,8)){break;}} }return 0;
}

迭代加深入门第五题------DNA序列
HDU1560
题目大意就是说给你n个长度不超过8的DNA序列,找出一个父序列使得这些原有的序列都是他的子序列。这里的子序列定义不严格,不一定要连续,只要每个子序列的字母在这个父序列中的相对位置没改变就可以。拿到题虽然读的有点费解但是仔细一想,题目一定有解哇。最差的父序列就是原来的子序列全部合并就是嘛。但是题目要求父序列的长度最短,好吧编不下去了,很明显的迭代加深搜索。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
struct info{char str[15];int len,top;
}s[15];
char way[] = {'A','C','G','T'};
int n;
int get_h()         //乐观估计
{int num = 0;for(int i = 0;i < n;i++){   //如果每个序列的len == top代表可以结束搜索了 num = max(num,s[i].len - s[i].top);}return num;
}
bool iddfs(int d,int maxd)
{if(d > maxd || d + get_h() > maxd){     //超过最大深度return false;       //或者乐观估计之后也不能达到目标结果 }if(get_h() == 0){            //达到目标状态 return true;}for(int i = 0;i < 4;i++){bool flag = false;bool visited[15];memset(visited,false,sizeof(visited));for(int j = 0;j < n;j++){      if(way[i] == s[j].str[s[j].top]){s[j].top++;                //相对位置后移 visited[j] = true;        //标记这个序列移动过top值 flag = true;           //回溯的时候要减回来 }}if(!flag){        //该字符没有影响任何一个字符的top后移直接跳掉 continue;}if(iddfs(d+1,maxd)){return true;} for(int j = 0;j < n;j++){      //回溯,top回到原来的值 if(visited[j]){s[j].top--;}}}return false;
}
int main()
{int t;scanf("%d",&t);while(t--){scanf("%d",&n);memset(s,0,sizeof(s));int num = -1;for(int i = 0;i < n;i++){scanf("%s",s[i].str);int len = strlen(s[i].str);s[i].len = len;num = max(num,len);      //找出最大长度,做为起始深度。 }for(int i = num; true ;i++){for(int j = 0;j < n;j++){s[j].top = 0;}if(iddfs(0,i)){printf("%d\n",i);break;}}}return 0;
}

搜索进阶之迭代加深搜索相关推荐

  1. NUIST OJ 1347 供电站 [迭代加深搜索]

    题目 题目分析 本题的坑 整体代码与运行结果 题目 题目描述 你一个程序员,不知为何就当上了你们镇的镇长(人的一生当然要靠自我奋斗,当然也要考虑历史的进程).你们镇有 N (3 <= N < ...

  2. 启发式搜索 迭代加深搜索 搜索对象的压缩存储

    常见的几种搜索算法 常见的几种搜索算法_唐宋缘明卿_cris的博客-CSDN博客_搜索算法有哪些 搜索 -- 启发式搜索 搜索 -- 启发式搜索_Alex_McAvoy的博客-CSDN博客_启发式搜索 ...

  3. hdu 1560 DNA sequence(迭代加深搜索)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1560 题意:从n个串中找出一个最短的公共串,,该公共串对于n个字符串不要求连续,即只要保持相对顺序就好 ...

  4. 紫书搜索 习题7-8 UVA - 12107 Digit Puzzle IDA*迭代加深搜索

    题目链接: https://vjudge.net/problem/UVA-12107 题意: 给出一个数字谜,要求修改尽量少的数,使修改后的数字谜只有唯一解.空格和数字可以随意替换,但不能增删,数字谜 ...

  5. UVA - 11214Guarding the Chessboard守卫棋盘(迭代加深搜索)

    题意:输入一个n*m棋盘(0<n,m<10),某些格子有标记.用最少的皇后守卫所有带标记的格子.皇后规则是所在坐标的直线和斜线都可以被守卫,长度不限. 分析:因为不知道深度,所以用迭代加深 ...

  6. 迭代加深搜索与埃及分数求解

    迭代加深搜索,实质上是限定下界的深度优先搜索.即首先允许深度优先搜索K层,若没有发现可行解,再将K+1后 重复以上步骤搜索,直到搜索到可行解. 在迭代加深搜索的算法中,连续的深度优先搜索被引入,每一个 ...

  7. P2324 [SCOI2005]骑士精神(迭代加深搜索,dfs)

    传送门 文章目录 解析 解析 很显然,让马走的话状态记录和转移都会比较复杂 所以转化成让空位跳会更好做一点 但这不是重点 初看本题,其实第一感觉是bfs 但是状态数理论上最差可以达到815,(当然基本 ...

  8. 迭代加深搜索 C++解题报告 :[SCOI2005]骑士精神

    题目 此题根据题目可知是迭代加深搜索. 首先应该枚举空格的位置,让空格像一个马一样移动. 但迭代加深搜索之后时间复杂度还是非常的高,根本过不了题. 感觉也想不出什么减枝,于是便要用到了乐观估计函数(O ...

  9. 算法复习——迭代加深搜索(骑士精神bzoj1085)

    题目: Description 在一个5×5的棋盘上有12个白色的骑士和12个黑色的骑士, 且有一个空位.在任何时候一个骑士都能按照骑 士的走法(它可以走到和它横坐标相差为1,纵坐标相差为2或者横坐标 ...

最新文章

  1. Pytorch完成基础的模型-线性回归
  2. pandas计算滑动窗口中的中位数实战(Rolling Median of a Pandas Column):计算单数据列滑动窗口中的中位数、计算多数据列滑动窗口中的中位数
  3. SAP RETAIL分配表的查询报表
  4. 设置Kali Linux虚拟机连接网络
  5. 基于HTML5的WebGL呈现A星算法的3D可视化
  6. Linux下Nginx访问web目录提示403Forbidden
  7. HTTP CSP详解
  8. AS3之BitmapData学习整理
  9. 143.根据文件头判断类型
  10. java连接数据库实现一个简单的登陆界面
  11. python筛选同义词_Python-比较同义词NLTK
  12. 怎么把QQ音乐里wav格式转换成MP3
  13. Office2021安装全教程
  14. java生日显示生肖星座_根据生日或者日期 获取 生肖和星座的 Java代码
  15. ROSERROR : C++ filt命令
  16. leetcode——【猫和老鼠】
  17. 使用fastboot命令刷机
  18. 使用matlab绘画曲线图,6.利用Matlab绘制趋势面图形.doc
  19. 使用UIDataDetectorTypes自动检测电话、网址和邮箱
  20. android 解析json 日期格式,如何将JSON格式的日期字符串解析为日期格式

热门文章

  1. 【斜率优化】【决策单调】xjb讲课
  2. mac mini u盘安装系统_用u盘安装win7系统详细步骤
  3. 软件测试--白盒测试、黑盒测试、α测试、β测试
  4. 移动直播进入下半场盈利为王,突破打赏模式成关键
  5. idenet 学习记录:bili
  6. jsp+ssh2+mysql实现的CRM客户关系管理系统
  7. 2021年11月线上消费信贷市场用户洞察:头部效应明显,绿色金融成新风口
  8. ACE1.0动态搜索框OpenHarmony组件开发大赛参与组件-Search_DialogJS
  9. 北京电子科技学院计算机考研资料汇总
  10. Nelder Mead SIMPLEX Algorithm