IDDFS(迭代加深搜索)精选题and总结



引入:什么是IDDFS?

  在计算机科学中,迭代深化搜索(iterative deepening search)或者更确切地说迭代深化深度优先搜索 (iterative deepening depth-first search (IDS or IDDFS)) 是一个状态空间(状态图)搜索策略。在这个搜索策略中,一个具有深度限制的深度优先搜索算法会不断重复地运行,并且同时放宽对于搜索深度的限制,直到找到目标状态。IDDFS 与广度优先算法是等价的,但对内存的使用会少很多;在每一步迭代中,它会按深度优先算法中的顺序,遍历搜索树中的节点,但第一次访问节点的累积顺序实际上是广度优先的。

——百度百科



一:一般搜索。

·题目描述:

  Luogu P1605 迷宫[原题链接]

·解析:

这一题差不多就是普通DFS的入门题了,适合深搜初学者做。

小技巧:标记和方向数组。

其中标记可以大大减少重复走过的路径,有效地加快搜索效率。

方向数组针对的是这题只要侦测4个方向即可,用一个for循环可以较大的简短代码长度。

其他的,想必大家都了解,此处就不再赘述了。

代码如下:

#include<bits/stdc++.h>
using namespace std;
int n,m,t;
int step[36][36];//记录走过的地方
int maps[6][6];//地图
int sx,sy;//起点
int ex,ey;//终点
int dx[1001],dy[1001];//障碍x、y坐标
int a[5]={0,0,0,-1,1},b[5]={0,-1,1,0,0};//上下左右
int total=0;

int check(int mx,int my)
{
    if(mx>=1&&my>=1&&mx<=n&&my<=m)
        if(maps[mx][my]==0)
            if(step[mx][my]==0)
                return 1;
    return 0;
}

void search(int x,int y)
{
    if(x==ex&&y==ey)
    {
        total++;
        return;
    }
    else
    {
        for(int i=1;i<=4;i++)
        {
            x+=a[i];
            y+=b[i];
            if(check(x,y)==1)
            {
                step[x][y]=1;
                search(x,y);
                step[x][y]=0;
                x-=a[i];
                y-=b[i];
            }
            else
            {
                x-=a[i];
                y-=b[i];
            }
        }
    }
}

int main()
{
    scanf("%d%d%d",&n,&m,&t);
    scanf("%d%d%d%d",&sx,&sy,&ex,&ey);
    for(int i=1;i<=t;i++)
    {
        scanf("%d%d",&dx[i],&dy[i]);
        maps[dx[i]][dy[i]]=1;
    }
    step[sx][sy]=1;
    search(sx,sy);
    printf("%d",total);
}


二:迭代加深搜索。

· 题目描述

  Luogu P3869 [TJOI2009]宝藏[原题链接]

· 解析

这一题似乎和上一题不太一样啊——加了一个什么“机关”。

但仔细理解理解,还是蛮好懂的。其实纯粹的DFS是过不了的。

原因:

  此题求解最短路径,用穷竭所有情况的DFS大为不利。也许你会说:BFS!这里有一个缺陷,我们知道,BFS需要维护一个队列,以进行“地毯式搜索”。但如果遇到了这种情况……

  如图,若右下角的‘@’按下机关,紧接着上面的‘@’进入了,就导致了秩序混乱,产生了错误答案。通俗点讲,就是“森林冰火人”中的常用策略。(此处是个错误)

突然没了思路……

现在,就要使用IDDFS了!

首先,定义一个变量叫maxstep,将DFS函数变成bool形,

那么DFS()的定义就成了:在maxstep步内能否找到答案。再用一个for循环,每次maxstep++。即:

for(maxstep=1;!Dfs(sx,sy,0);maxstep++);

maxstep可以控制深度,防止递归到爆栈。

最后只需输出maxstep即可。

另外,我们还发现了有许多情况是搜过许多遍的,所以,在刚刚递归到下一层时,可以先将当前状态Hash一下,存入一个map(Hash值为key,步数为value)中。在这之前,先要判断一下map中是否有更优解,如果有,直接返回;没有,将状态和步数存入map再继续。

每次maxstep++的时候,同时清空map。

代码如下:

#include<bits/stdc++.h>
#define hash Do_not_use_hash
using namespace std;
typedef unsigned long long ull;
const int base=1e9+1;
map<ull,int> M;
int a[31][31];
int n,m,k;
int d[11][2];
int t[11][2];
int sx,sy,ex,ey;
int maxstep;
const int dx[5]= {0,0,0,1,-1},dy[5]= {0,1,-1};

ull hash(int x,int y)
{
    ull temp=0;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            if(a[i][j]==0)
                temp=temp*base+1;
            else
                temp=temp*base+2;
        }
    temp=temp*base+x;
    temp=temp*base+y;
    return temp;
}

inline int calc(int x,int y)
{
    return abs(ex-x)+abs(ey-y);
}

bool Dfs(int x,int y,int step)
{
  ull now=hash(x,y);
  if(M.find(now)!=M.end()&&M[now]<=step)
      return 0;
  M[now]=step;
    if(ex==x&&y==ey)
        return 1;
    if(step>=maxstep||step+calc(x,y)>maxstep)
        return 0;
    for(int i=1;i<=4;i++)
    {
        x+=dx[i],y+=dy[i];
        if(x>=1&&y>=1&&x<=n&&y<=m&&!a[x][y])
        {
            for(int h=1;h<=k;h++)
                if(d[h][0]==x&&d[h][1]==y)
                    a[t[h][0]][t[h][1]]^=1;
            if(Dfs(x,y,step+1))
            {
                for(int h=1;h<=k;h++)
                    if(d[h][0]==x&&d[h][1]==y)
                        a[t[h][0]][t[h][1]]^=1;
                x-=dx[i],y-=dy[i];
                return 1;
            }
            else
            {
                for(int h=1;h<=k;h++)
                    if(d[h][0]==x&&d[h][1]==y)
                        a[t[h][0]][t[h][1]]^=1;
            }
        }
        x-=dx[i],y-=dy[i];
    }
    return 0;
}

int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            char temp;
            cin>>temp;
            if(temp==‘#‘)
                a[i][j]=1;
            else
                a[i][j]=0;
            if(temp==‘S‘)
                sx=i,sy=j;
            if(temp==‘T‘)
                ex=i,ey=j;
        }
    cin>>k;
    for(int i=1;i<=k;i++)
        cin>>d[i][0]>>d[i][1]>>t[i][0]>>t[i][1];
    for(maxstep=1;!Dfs(sx,sy,0);maxstep++,M.clear());
    cout<<maxstep;
    return 0;
}

备注:calc是一个剪枝(即IDA*的体现,以后会提到)。该代码直接提交只有80分,也算是全Luogu第二了。(实在不会优化了,还望指教)

三:总结:

外加几个tips:

  IDDfs的前提:一定要有解。

  注意DFS是bool型,而不是void型。

IDDFS的优势:  

1.时间复杂度只比BFS稍差一点(虽然搜索k+1层时会重复搜索k层,但是整体而言并不比广搜慢很多)。

2.空间复杂度与深搜相同,却比广搜小很多。

3.利于剪枝。

·经典必练

  Luogu P2324 [SCOI2005]骑士精神 [原题链接]

原文地址:https://www.cnblogs.com/-Wallace-/p/9865169.html

时间: 2024-08-04 15:25:15

IDDFS(迭代加深搜索)精选题and总结的相关文章

uva 11212 - Editing a Book(迭代加深搜索 IDA*) 迭代加深搜索

迭代加深搜索 自己看的时候第一遍更本就看不懂..是很水,但智商捉急也是没有办法的事情. 好在有几个同学已经是做过了这道题并且对迭代加深搜索的思路有了一定的了解,所以在某些不理解的地方询问了一下他们的见解, 真的是很有帮助,也许自己想要想很久才能想明白,还会很痛苦,稍微问一下别人的想法,点上一个方向,剩下的自己就能想得明白了. 迭代加深. 把answer(需要的步数或其他)在主函数里面从零往上递加,此之谓 "层数",亦可谓之"深度".用书上的话就是: 从小到大枚举深度

搜索专题小结:迭代加深搜索

迭代加深搜索 迭代加深搜索(Iterative Deepening Depth-First Search, IDDFS)经常用于理论上解答树深度上没有上界的问题,这类问题通常要求出满足某些条件时的解即可.比如在"埃及分数"问题中要求将一个分数a/b分解成为若干个形如1/d的加数之和,而且加数越少越好,如果加数个数相同,那么最小的分数越大越好.下面总结一下该方法的一般流程: (1)概述:迭代加深搜索是通过限制每次dfs的最大深度进行的搜索.令maxd表示最大的搜索深度,那么dfs就只能在

“埃及分数”问题浅谈对迭代加深搜索的理解

迭代加深搜索(IDDFS)的思想 迭代加深搜索一般用来求解状态树"非常深",甚至深度可能趋于无穷,但是"目标状态浅"的问题.如果用普通的DFS去求解,往往效率不够高.此时我们可以对DFS进行一些改进.最直观的一种办法是增加一个搜索的最大深度限制maxd,一般是从1开始.每次搜索都要在maxd深度之内进行,如果没有找到解,就继续增大maxd,直到成功找到解,然后break. 如下图所示,如果用DFS,需要15步才能找到结点3,但是用迭代加深搜索,很快即可找到结点3.

Codevs 四子连棋 (迭代加深搜索)

题目描述 Description 在一个4*4的棋盘上摆放了14颗棋子,其中有7颗白色棋子,7颗黑色棋子,有两个空白地带,任何一颗黑白棋子都可以向上下左右四个方向移动到相邻的空格,这叫行棋一步,黑白双方交替走棋,任意一方可以先走,如果某个时刻使得任意一种颜色的棋子形成四个一线(包括斜线),这样的状态为目标棋局. ● ○ ●   ○ ● ○ ● ● ○ ● ○ ○ ● ○   输入描述 Input Description 从文件中读入一个4*4的初始棋局,黑棋子用B表示,白棋子用W表示,空格地带用

C++解题报告 : 迭代加深搜索之 ZOJ 1937 Addition Chains

此题不难,主要思路便是IDDFS(迭代加深搜索),关键在于优化. 一个IDDFS的简单介绍,没有了解的同学可以看看: https://www.cnblogs.com/MisakaMKT/articles/10767945.html 我们可以这么想,设当前规定长度为M,题目要求得出的数为N. 在搜索中,当前的步数为step,当前的数列为 数组a. 首先来确定思路,便是在以得出的数列a中枚举每两个数相加得出sum,然后继续搜索下一步. 初步的代码便是: void iddfs(int x) { for

UVA-11214 Guarding the Chessboard (迭代加深搜索)

题目大意:在一个国际象棋盘上放置皇后,使得目标全部被占领,求最少的皇后个数. 题目分析:迭代加深搜索,否则超时. 小技巧:用vis[0][r].vis[1][c].vis[2][r+c].vis[c-r+N]分别标志(r,c)位置相对应的行.列.主.副对角线有没有被占领(详见<入门经典(第2版)>P193),其中N表示任意一个比行数和列数都大(大于等于)的数. 代码如下: # include<iostream> # include<cstdio> # include&l

USACO/fence8 迭代加深搜索+剪枝

题目链接 迭代加深搜索思想. 枚举答案K,考虑到能否切出K个木头,那么我们当然选最小的K个来切. 1.对于原材料,我们是首选最大的还是最小的?显然,首选大的能够更容易切出,也更容易得到答案. 2.对于目标木头,我们是优先得到最大的还是最小的?显然,由于K个木头我们都要得到,那么当然先把最大的(最难得到的)先得到,这种搜索策略更优. 3.假设总原材料为all,前K个木头总和为sum,那么all-sum就是这一次切割过程中能[浪费]的最大数目.对于一个切剩下的原材料,若它比最小的目标木头还要小,则它

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

DNA sequence Time Limit : 15000/5000ms (Java/Other)   Memory Limit : 32768/32768K (Java/Other) Total Submission(s) : 15   Accepted Submission(s) : 7 Font: Times New Roman | Verdana | Georgia Font Size: ← → Problem Description The twenty-first century

【迭代加深搜索】埃及分数问题

谢谢阿苏~http://blog.csdn.net/urecvbnkuhbh_54245df/article/details/5856756 [迭代加深搜索(ID,iterative deepening)]:从小到大枚举上限maxd,每次执行只考虑深度不超过maxd的结点. ------对于可以用回溯法求解但解答树的深度没有明显上限的题目,可以考虑ID算法: ------优点:它主要是在递归搜索函数的开头判断当前搜索的深度是否大于预定义的最大搜索深度,如果大于,就退出这一层的搜索,如果不大于,就