dfs&bfs

1.dfs
深度优先搜索:
常用递归实现,先画出递归树,深度优先指的是按照一条路线一直走到头,然后回溯,在回溯过程中还要检查是否有其他路径,如果有则继续走:
(递归还没学明白的小可爱去看杂文那一篇。。。。)
1.全排列问题:枚举并记录记录每个数字的状态,对已经使用过的数字做标记

int p[N],s[N];
int m;
void dfs(int n){
	if(n==m){//终止条件
	for(int i=0;i<n;i++) printf("%d ",p[i]);//输出
	puts("");
	return}
	for(int i=1;i<=m;i++)//枚举
		if(!s[i]){
			p[n]=i;//将i赋值给当前位置存储
			s[i]=1;//改变状态,
			dfs(n+1);//进行下一个位置的查找
			s[i]=0;//回溯过程中清除记录;
		}
}

2.递归枚举:

#include<iostream>
using namespace std;
int n;
int s[20];
void dfs(int m){
    if(m==n){
        for(int i=0;i<n;i++)
            if(s[i]==1) printf("%d ",i+1);
        puts("");
        return;
    }
    s[m]=1;//状态选择,1表示选择
    dfs(m+1);
    s[m]=0;//0表示没有选择;

    s[m]=2;//2表示不选;
    dfs(m+1);
    s[m]=0;
}
int main(){
    cin>>n;
    dfs(0);



    return 0;
}

回溯:在执行完最后一层递归之后肯定会进行回溯(可以用栈的思想来理解),回溯过程中就要清楚记录
部分和问题
输入一个数字n,然后输入n个数字和一个数字k,在数组中挑选若干个数字,使得总和刚好是k;
状态分析 dfs(n,sum)在前n个中选择,和为sum;

#include<iostream>
using namespace std;

int a[25];
int n,k;
bool dfs(int m,int sum){
    if(m==n) return sum==k;//所有数字已经选完(不一定全部都选,分选与不选的情况),判断和是不是等于k
    if(dfs(m+1,sum) or dfs(m+1,sum+a[m])) return true;//可以理解到为执行完不选择之后
    //假如为false,返回到上一层,然后看选择这个数字的方案。
    return false;
}

int main(){
    cin>>n;
    for(int i=0;i<n;i++) cin>>a[i];
    cin>>k;
    if(dfs(0,0)) puts("YES");
    else puts("NO");
    return 0;
}

n皇后问题:题目不做叙述;
解法一(全排列解法):
在n*n的棋盘上,每一列有一个皇后(对应y轴),然后只需要判断对应x轴是否已经选数字和对应的两条对角线上是否有数字即可;

#include<bits/stdc++.h>
using namespace std;
const int N=20;
int n;
char g[N][N];//棋盘;
bool a[N],b[N],c[N];
void dfs(int u){//u表示列;
    if(u==n){
        for(int i=0;i<n;i++) puts(g[i]);
        puts("");
    }
    for(int i=0;i<n;i++)
        if(!a[i] and !b[u+i] and !c[n+u-i]){//考虑到u-i可能成负数的情况;
            g[u][i]='Q';//数组b:y=-x+b,//数组c:y=x+b;数组a表示行;
            a[i]=b[u+i]=c[n+u-i]=true;
            dfs(u+1);
            a[i]=b[u+i]=c[n+u-i]=false;//回溯过程;
            g[u][i]='.';

        }
}
int main(){
    cin>>n;
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            g[i][j]='.';
    dfs(0);
    return 0;
}

解法二:枚举每个格子,直到枚举到n^2;

#include<bits/stdc++.h>
using namespace std;
int n;
const int N=20;
bool row[N],col[N],dg[N],udg[N];
char g[N][N];
void dfs(int x,int y,int s){//x,y表示行和列,s表示皇后个数
    if(x==n) x=0,y++;//当x=n时表示一行已经枚举完成,直接跳到下一行
    if(y==n){//y=n表示已经全部枚举完;
        if(s==n){
        	for(int i=0;i<n;i++) puts(g[i]);
        	puts("");
		}
        return;
    }
    dfs(x+1,y,s);//不放皇后的情况
    if(!row[y] and !col[x] and !dg[x+y] and !udg[x-y+n]){//放置皇后时需要进行判断,行列对角线都不能有皇后;
        g[y][x]='Q';
        row[y]=col[x]=dg[x+y]=udg[x-y+n]=true;
        dfs(x+1,y,s+1);
        row[y]=col[x]=dg[x+y]=udg[x-y+n]=false;//回溯:
        g[y][x]='.';
    }
}
int main(){
    cin>>n;
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            g[i][j]='.';
    dfs(0,0,0);
    return 0;
}

bfs宽度优先搜索
一层一层的走,直到走完,可以和深搜做对比记忆;
走迷宫问题:
在这里插入图片描述

当队列不为空,扩展队头;

#include<bits/stdc++.h>
using namespace std;
const int N=110;
int d[N][N],g[N][N];//g用来存储迷宫,d表示每个点到起点的距离;
int n,m;
typedef pair<int,int> pii;//存储坐标;
int bfs(){
    memset(d,-1,sizeof d);
    queue<pii>a;
    a.push({0,0});//第一个入队;
    d[0][0]=0;//当前位置距离起点距离为0;
    while(!a.empty()){
        auto t=a.front();//取出队头;
        a.pop();
        int dx[4]={-1,1,0,0},dy[4]={0,0,-1,1};//分别表示上下左右;
        //扩展队头;
        for(int i=0;i<4;i++){
            int x=t.first+dx[i],y=t.second+dy[i];
            if(x>=0 and x<n and y>=0 and y<m and !g[x][y] and d[x][y]==-1){//x<n,y<m判断边界,!g[x][y]表示路径,d[x][y]表示未走过的点;
                d[x][y]=d[t.first][t.second]+1;//经过这个点;
                a.push({x,y});
            }
        }
    }
    return d[n-1][m-1];
}
int main(){
    cin>>n>>m;
    for(int i=0;i<n;i++)
        for(int j=0;j<m;j++)
            cin>>g[i][j];
    cout<<bfs()<<endl;

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值