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

Posted -wallace-

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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]骑士精神 [原题链接]

 

以上是关于IDDFS(迭代加深搜索)精选题and总结的主要内容,如果未能解决你的问题,请参考以下文章

Loj10022 埃及分数(迭代加深搜索IDDFS)

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

POJ 3134 IDDFS

POJ 3134 - Power Calculus (IDDFS)

[IDDFS+背包] 洛谷P2744 [USACO5.3]量取牛奶Milk Measuring

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