ACwing算法备战蓝桥杯——Day13——DFS

文章通过深度优先搜索(DFS)展示了如何解决排列问题,n皇后问题以及不同路径数问题。在排列问题中,使用DFS按字典序输出所有排列;在n皇后问题中,结合剪枝策略避免重复计算;不同路径数问题同样利用DFS暴力搜索求解。每个问题都提供了代码示例,说明了DFS在处理这些组合优化问题时的效率和灵活性。
摘要由CSDN通过智能技术生成

深度优先搜索,简称DFS.

实质上就是递归,代码写法也是递归函数.

模板:

模板:
    int dfs(int u)
{
    st[u] = true; // st[u] 表示点u已经被遍历过

    for (int i = h[u]; i != -1; i = ne[i])
    {
        int j = e[i];
        if (!st[j]) dfs(j);
    }
}

经典例题:

排列数字:

给定一个整数  n ,将数字 1∼n 排成一排,将会有很多种排列方法。

现在,请你按照字典序将所有的排列方法输出。

输入格式
共一行,包含一个整数 n。

输出格式
按字典序输出所有排列方案,每个方案占一行。

数据范围
1≤n≤7
输入样例:
3
输出样例:
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1

代码及题解:
//数据范围(n<30,指数级=>dfs加减枝,状态压缩)
#include <iostream>
using namespace std;
const int N=10;
int n;
int path[N];
bool st[N];
void dfs(int u){//从u结点开始
    if(u==n){
        for(int i=0;i<n;i++){
            cout<<path[i]<<' ';
        }
        puts("");
        return;
    }
    for(int i=1;i<=n;i++){
        if(st[i]) continue;
        path[u]=i;
        st[i]=true;
        dfs(u+1);
        st[i]=false;
    }
}
int main(){
    cin>>n;
    dfs(0);//从0结点开始
    return 0;
}

n皇后:

n−皇后问题是指将 n 个皇后放在 n×n 的国际象棋棋盘上,使得皇后不能相互攻击到,即任意两个皇后都不能处于同一行、同一列或同一斜线上。
现在给定整数 n,请你输出所有的满足条件的棋子摆法。

输入格式
共一行,包含整数 n。

输出格式
每个解决方案占 n 行,每行输出一个长度为 n 的字符串,用来表示完整的棋盘状态。

其中 . 表示某一个位置的方格状态为空,Q 表示某一个位置的方格上摆着皇后。

每个方案输出完成后,输出一个空行。

注意:行末不能有多余空格。

输出方案的顺序任意,只要不重复且没有遗漏即可。

数据范围
1≤n≤9
输入样例:
4
输出样例:
.Q..
...Q
Q...
..Q.

..Q.
Q...
...Q
.Q..

代码及题解:
//数据范围,n<30,指数级别,dfs+剪枝,状态压缩DP
#include <iostream>
#include <cstring>
using namespace std;
const int N=20;
char q[N][N];
int st[N][N];//状态数组,存的树大于0表示遍历过,小于等于0表示未遍历过
int n;
void Chang(int a,int b,int v){//将i,j点的横,竖,斜三条线上的st设为遍历过,v表示是删还是加
    for(int j=0;j<n;j++){//横行
        st[a][j]+=v;
    }
    for(int i=0;i<n;i++){//竖行
        st[i][b]+=v;
    }
    int c1=a+b,c2=a-b;//两个斜率绝对值为1的两条斜线的截距
    for(int i=0;i<n;i++){//斜线上的状态进行处理
        int j1=c1-i,j2=i-c2;
        if(j1>=0&&j1<n) st[i][j1]+=v;
        if(j2>=0&&j2<n) st[i][j2]+=v;
    }
}
void dfs(int u){
    if(u==n){//遍历到n层就结束
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++){//打印数组
                printf("%c",q[i][j]);
            }
            puts("");
        }
        cout<<endl;
        return;
    }
    for(int j=0;j<n;j++){
        if(st[u][j]>0) continue;
        q[u][j]='Q';
        Chang(u,j,1);//改变状态
        dfs(u+1);//深搜u+1层
        q[u][j]='.';//还原状态
        Chang(u,j,-1);
    }
}
int main(){
    cin>>n;
    memset(q,'.',sizeof(q));//数组初始化为'.'
    dfs(0);
    return 0;
}

不同路径数:

给定一个 n×m 的二维矩阵,其中的每个元素都是一个 [1,9] 之间的正整数。

从矩阵中的任意位置出发,每次可以沿上下左右四个方向前进一步,走过的位置可以重复走。

走了 k 次后,经过的元素会构成一个 (k+1) 位数。

请求出一共可以走出多少个不同的 (k+1) 位数。

输入格式
第一行包含三个整数 n,m,k。

接下来 n 行,每行包含 m 个空格隔开的整数,表示给定矩阵。

输出格式
输出一个整数,表示可以走出的不同 (k+1) 位数的个数。

数据范围
对于 30% 的数据, 1≤n,m≤2,0≤k≤2
对于 100% 的数据,1≤n,m≤5,0≤k≤5,m×n>1
输入样例:
3 3 2
1 1 1
1 1 1
2 1 1
输出样例:
5
样例解释
一共有 5 种可能的 3 位数:

111
112
121
211
212
//这题用BFS也行
代码:
#include <iostream>
#include <unordered_set>
#include <string>
using namespace std;
const int N=6;
int q[N][N];//存数据
int dx[4]={1,-1,0,0},dy[4]={0,0,-1,1};//往四个方向上的偏移量
int n,m,k;
int path[7];//路径数组
unordered_set<string> anws;//储存某个k+1位数
void dfs(int i,int j,int u){//深度优先遍历(暴力搜索,枚举每个状态)
    path[u]=q[i][j];
    if(u==k){//递归结束条件,当遍历了k个数,path数组里存了k+1位数
        string str;
        for(int o=0;o<=k;o++){
            str+=(char)(path[o]+'0');//转换成字符串储存在anws里
        }
        anws.insert(str);
        return;
    }
    for(int o=0;o<4;o++){//枚举四个方向
        int x1=i+dx[o],y1=j+dy[o];
        if(x1<n&&x1>=0&&y1<m&&y1>=0){//如果当前结点合法,就继续深入遍历
            path[u+1]=q[x1][y1];
            dfs(x1,y1,u+1);
        }
    }
}
int main(){
    cin>>n>>m>>k;
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            scanf("%d",&q[i][j]);
        }
    }
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){//枚举以每个点为根结点的DFS
            int u=0;
            dfs(i,j,u);
        }
    }
    cout<<anws.size()<<endl;//anws里的
    return 0;
}

以上都是本人的做题理解,如有不当的地方,请多多指教😃!!!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

切勿踌躇不前

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值