算法导论机考复习(深度优先搜索)

深度优先搜索

dfs函数:

1.明确dfs函数的参数是什么意思

2.设置递归边界,什么时候输出一种答案

3.某一层的dfs没有到达递归边界怎么进行循环来尝试这一层的所有可能操作

4.进行完一个分支之后回溯把book设置为0,在全排列问题中是为了防止不设置为0的话根本无法获得n个数。

全排列问题

//****深度优先搜索****
//全排列问题
#include<bits/stdc++.h>
using namespace std;
const int N=1000;
int book[N],n,a[N];
void dfs(int num)//num不是表示数据是表示已经有几个数了
{
    if(num>n)//符合递归边界就输出
    {
        for(int i=1;i<=n;i++)
        {
            cout<<a[i]<<" ";
        }
        cout<<endl;
//这样写的话就会一直输出一个特定的结果了
//所以需要设置一个数组存结果
//        for(int i=1;i<=n;i++)
//        {
//            if(book[i])
//            {
//               cout<<i<<" ";
//            }
//        }
    }
    else//还可以继续探索就先把当前点设置为已访问
    {
       for(int i=1;i<=n;i++)
       {
           if(!book[i])
           {
               a[num]=i;
               book[i]=1;
               dfs(num+1);
               book[i]=0;
           }
       }
    }
}


int main()
{
    cin>>n;
    dfs(1);
    return 0;
}

组合问题:主体与排列问题差不多,用数组存数,到边界就输出,

但是在递归过程中,有一个要求因为1,2和2,1这个排列相同。

所以组合问题与排列问题最大的区别是,在一层dfs中的for循环中,全排列只要找n个数里面还没有被找到的那个点即可,但是组合问题要求要从上一个数的后面去找,这样就不会出现1,2和2,1了

所以dfs里面加上一个参数来表示:前面的组合数的后一个树,应该从这开始向后找

//****深度优先搜索****
//组合问题
#include<bits/stdc++.h>
using namespace std;
const int N=1000;
int book[N],n,a[N],m;
void dfs(int num,int num2)//num不是表示数据是表示已经有几个数了
{
    if(num>m)//符合递归边界就输出
    {
        for(int i=1;i<=m;i++)
        {
            cout<<a[i]<<" ";
        }
        cout<<endl;

    }
    else//还可以继续探索就先把当前点设置为已访问
    {
       for(int i=num2;i<=n;i++)
       {
           if(!book[i])
           {
               a[num]=i;
               book[i]=1;
               dfs(num+1,i+1);
               book[i]=0;
           }
       }
    }
}


int main()
{
    cin>>n>>m;
    dfs(1,1);
    return 0;
}

发现深度优先搜索比动态规划的计算的思路过程复杂,但是  深度优先搜索做题很好做。

对于N皇后问题,不能在同行,同列,同一条主副对角线,都是同样的模板,

dfs函数的参数就是已经运行到第几行了

递归边界就是num>n

递归中对dfs这行的所有可能的列进行测试,符合条件就直接标记好,然后继续去看下一行,

//****深度优先搜索****
//N皇后问题
#include<bits/stdc++.h>
using namespace std;
const int N=1000;
int book[N],n,a[N];
int zhudui[N],fudui[N];
int sum;
void dfs(int num)
{
    if(num>n)//到第几行了
    {
        sum++;
    }
    else
    {
        for(int i=1;i<=n;i++)//第num行的第i列
        {
            if(!book[i]&&!zhudui[num-i+n]&&!fudui[num+i])
            {//找到一个符合条件的点事这行这列这两条对角线上的第一个点
                book[i]=1;//这一列有了
                zhudui[num-i+n]=fudui[num+i]=1;
                dfs(num+1);//在这一列有的情况之下继续进行下一层
                zhudui[num-i+n]=fudui[num+i]=book[i]=0;//回溯
            }
        }
    }
}
int main()
{
    cin>>n;
    dfs(1);
    cout<<sum<<endl;
    return 0;
}

迷宫问题

前面的组合问题和排列问题的一个区别就是下一层的dfs函数里面的循环必须在上一层的下一个才可以,所以在dfs里面添加了一个参数:表示上一层选择的数的下一个

在迷宫问题里面,还是在dfs里面每次循环基于的是上一个点的左右上下,所以在dfs里面除了num这个表示步数的,还有xy表示dfs里面遍历的起点,也就是上一层里面找的或者左右或者上下的一个点,在这一层里面再去考虑符合条件的点(这里很容易可以看出在下一层dfs之前为什么要让book==1了);

//****深度优先搜索****
//迷宫问题求解
//很容易想到dfs里面的遍历是对当前点的周围所有点的尝试
#include<bits/stdc++.h>
using namespace std;
const int N=1000;
int book[N][N];
int minn=100000,sx,sy,m,n;
char s[N][N];
int f[4][2]={-1,0,1,0,0,1,0,-1};
int in(int xpos,int ypos)
{
    if(xpos<=m&&ypos<=n&&xpos>=0&&ypos>=0)
        return 1;
    return 0;
}
void dfs(int x,int y,int num)//走了几步
{
    if(s[x][y]=='T')
    {
        minn=min(minn,num);
    }
    else
    {
       int xpos,ypos;
       for(int i=0;i<4;i++)
       {
           xpos=x+f[i][0];
           ypos=y+f[i][1];
           if(in(xpos,ypos)&&!book[xpos][ypos]&&s[xpos][ypos]!='#')
           {
               book[xpos][ypos]=1;
               dfs(xpos,ypos,num+1);
                book[xpos][ypos]=0;
           }
       }
    }

}


int main()
{
    cin>>m>>n;
    //说明了几行几列就两层for输入
    for(int i=1;i<=m;i++)
    {
        for(int j=1;j<=n;j++)
        {
            cin>>s[i][j];
            if(s[i][j]=='S')//记录起点在哪里
            {
                sx=i;
                sy=j;
            }
        }
    }
    dfs(sx,sy,0);
    cout<<minn;

}

 

 不同问题的目的不一样:

N皇后问题:只有一个参数表示行数

全局上来看就是在图的所有行都放一个皇后;

递归边界就是最后一行放完了;

遍历的顺序是这一行的所有的点是否符合;

迷宫问题:两个参数即为xy坐标

全局上来看是为了找到一条从起点到终点的最短路径,或者根本没有路径;

递归边界就是找到了终点;

遍历的顺序是考察所有的前后左右的点;

棋盘问题:因为递归的边界是所有的棋子都放完了,必然有一个参数存棋子数,因为要不同行,不同列,所以为了维护这两个性质,再拿一个参数来存行数;

全局上来看是要把所有的棋子全部放到棋盘上;

递归边界是所有棋子都放完了;

棋盘问题要求是组合问题而不是排列问题,所以必须从当前行的后

一行开始遍历,

是一个两层的循环。

棋盘问题与组合问题类似,第一个参数都是第几个,第二个参数都是有几个了

但是在遍历的时候,组合问题只是从第几个的后面开始遍历,只要没有标记就放进结果数组

棋盘问题遍历的时候也是从之前行的下一行开始遍历,但是选择很多还要遍历当前行的所有列是否符合要求。所以是两层循环。

//****深度优先搜索****
//棋盘问题
#include<bits/stdc++.h>
using namespace std;
const int N=1000;
int book[N];
int sum,m,n;
char s[N][N];

void dfs(int num,int num2)//到了第几行了,还剩num个
{
    cout<<"目前是第"<<num<<endl;
    if(num2==0)
    {
        sum++;
    }
    else
    {
        for(int j=num; j<=n; j++)
        {//因为要求是组合问题,必须从上一层的下一个开始遍历

            for(int i=1; i<=n; i++) //遍历所有列
            {
                if(!book[i]&&s[j][i]=='.')
                {
                    book[i]=1;
                    dfs(num+1,num2-1);
                    book[i]=0;
                }
            }
        }
    }

}


int main()
{
    cin>>n>>m;
    //说明了几行几列就两层for输入
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=n; j++)
        {
            cin>>s[i][j];

        }
    }
    dfs(1,m);
    cout<<sum;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值