深度优先搜索(DFS) & 广度优先搜索(BFS)总结

深度优先

1.算法模板

void dfs(int i)	// 核心代码
{
    if ( 1.到达推出条件 ) {
       	// 搜索结束, 退出
    }
    for ( 2.遍历所有可能 ) {
        if ( 3.满足走向下一层条件 ) {
        	4.// 回溯开头
            5.dfs(i+1);
            6.// 回溯结尾
        }
    }
}

2.例题

2.1 n 皇后问题(洛谷)

// 假设 行列不超过 20
#include<bits/stdc++.h>

using namespace std;
// 标记数组
bool row[20], line[20], incline_left[40], incline_right[40];
int sum; // 总个数
int order[20];  // 存储答案顺序
int n; 
int times = 3; // 输出的个数

void print()
{
    sum++;
    if (times > 0) {
        cout << order[1];
        for (int i = 2; i <= n; i++)
            cout << ' ' << order[i];

        cout << endl;
    }
    times--;

}

void dfs(int i)	// 核心代码
{
    if (i > n) {
        print();
        return;
    }
    for (int j = 1; j <= n; j++) {
		
        if (!line[j] && !incline_left[i+j] && !incline_right[n-1-j+i]) {
			
            order[i] = j;
            line[j] = 1;
            incline_left[i + j] = 1;
            incline_right[n - 1 - j + i] = 1;
            dfs(i+1);
            line[j] = 0;
            incline_left[i + j] = 0;
            incline_right[n - 1 - j + i] = 0;
            order[i] = 0;
        }
    }
}

int main()
{
    cin >> n;
    dfs(1);
    cout << sum;
}

2.2 素数环(洛谷)

#include<iostream>

using namespace std;

int a[20] = {0,1}, isp[40];
int T, n;
bool vis[20] = {0,1};

void dfs(int x,int op)
{
	if(x == n+1 && isp[a[n]+a[1]]) {
		for(int i = 1; i < n; i++) printf("%d ",a[i]);
		printf("%d\n", a[n]);
	}
	if(x == n+1) return ;
	for(int i = op; i <= n; i += 2) //只搜奇数或偶数
		if(!vis[i] && isp[i+a[x-1]]) {
			a[x] = i;
			vis[i] = 1;
			dfs(x+1, op^3); // 1^3变为2,2^3变为1,用以保证相邻两个数为一奇一偶
			vis[i] = 0;
		}
}

int main()
{
	// 素数置为 1 
	isp[2] = isp[3]=isp[5]=isp[7]=isp[11]=isp[13]=isp[17]=isp[19]=isp[23]=isp[29]=isp[31]=1;
	while(scanf("%d",&n)!=EOF)
	{
		if(T) printf("\n");
		printf("Case %d:\n",++T);
		dfs(2,2); // 从 2, 2 开始搜索
	}
	return 0;
}

2.3 全排列(洛谷)

#include<bits/stdc++.h>
using namespace std;
int n,pd[100],used[100];//pd是判断是否用过这个数
void print()//输出函数
{
    int i;
    for(i=1;i<=n;i++)
    printf("%5d",used[i]);//保留五位常宽
    cout<<endl;
}
void dfs(int k)//深搜函数,当前是第k格
{
    int i;
    if(k==n) //填满了的时候
    {
        print();//输出当前解
        return;
    }
    for(i=1;i<=n;i++)//1-n循环填数
    {
        if(!pd[i])//如果当前数没有用过
        {
            pd[i]=1;//标记一下
            used[k+1]=i;//把这个数填入数组
            dfs(k+1);//填下一个
            pd[i]=0;//回溯
        }
    }
}
int main()
{
    cin>>n;
    dfs(0);//注意,这里是从第0格开始的!
    return 0;
}

2.4 马的遍历(洛谷)

// 注意到没,我这里是一个bfs算法,虽然用 dfs+标记数组 遍历也可, 但接下来是bfs的时间了啦
#include<bits/stdc++.h>
using namespace std;
struct queue_
{
    int x,y; // 一个结构体,x,y是队列该位置放的点的x,y值
} que[160010]; // 这里一定要注意数组大小,我在这里RE了两次!!
int head=0,tail=1,get[401][401],n,m,sx,sy;
int fx[16]={2,-2,2,-2,-1,1,-1,1},fy[16]={1,1,-1,-1,2,2,-2,-2}; // 方向
int main()
{
    cin >> n >> m >> sx >> sy;
    for(int i=1; i <= n; ++i)
        for(int j=1; j <= m; ++j)
            get[i][j] =-1; // 初始化
    get[sx][sy]=0; // 第一个点入队
    que[1].x=sx;
    que[1].y=sy;
    while(head < tail)
    {
        head++; // 头指针加1
        int s=get[que[head].x][que[head].y]+1; // 这个s是指扩展到新点时所需要的最少步数,就是上一个点的步数加1
        for(int i=0; i<8; ++i)
        {
            int nx = que[head].x+fx[i], ny=que[head].y+fy[i]; // 扩展新点
            if(nx>=1 && nx<=n && ny>=1 && ny<=m && get[nx][ny] == -1) // 没有超出棋盘并且没有走过
            {
                tail++;
                que[tail].x=nx;
                que[tail].y=ny; // 新点入队
                get[nx][ny]=s; // 标记到达该点的最小步数
            }
        }
    }
    
    for(int i=1; i<=n; ++i)
    {
        for(int j=1; j<=m; ++j)
            printf("%-5d", get[i][j]); // 输出,注意格式
        cout<<endl;
    }
    return 0; // 华丽丽地结束
}

广度优先

1.算法模板

1. queue<T> q; // 定义一个列队
2. q.push(head) // 头节点入队
3. whlie(!q.empty()) {// 弹出条件为列队为空
4. 		int T = q.top(); // 取出列队头
5. 		q.pop() // 出队
6.  	for ( 遍历所有可能 ) {
7. 			if ( 满足条件 ) {
8. 				q.push( now ) // 待扩展结点入队
9. 				// 进行一些瞎乱操作
10. 			}
11. 	}
12. }

2.例题

2.1 Dungeon Master(洛谷)

#include<bits/stdc++.h>
using namespace std;
int a[105][105][105],n,m,o;//数据较水,直接开三维数组 
char p;
int dx[15]={0,0,0,0,1,-1};/*三个方向的方向数组,一共上下左右前后六个拓展维度*/ 
int dy[15]={0,0,1,-1,0,0};
int dz[15]={1,-1,0,0,0,0};
struct	kk//结构体用于记录每个点的数据 
{
	int x;
	int y;
	int z;
	int step;
};
kk	q[1000005];//队列,拒绝STL 
bool check(int x,int y,int z)// 检验这个点是否越界,是否已经走过 
{
	if(x<1 || x>n || y<1 || y>m || z<1 || z>o)
		return 0;
	if(a[x][y][z])
		return 0;
	return 1;
}
void bfs(int x1,int y1,int z1,int x2,int y2,int z2)//bfs函数 
{
	int head=1,tail=1;//头尾指针初始化 
	bool flag=1;//是否有答案 
	q[1].x=x1;//第一个点入队 
	q[1].y=y1;
	q[1].z=z1;
	q[1].step=0;
	a[x1][y1][z1]=1;//地图标记 
	while(head<=tail)//广搜板子来了 
	{
		for(int i=0;i<6;i++)//循环六个方向 
		{
			int tx=q[head].x+dx[i];
			int ty=q[head].y+dy[i];
			int tz=q[head].z+dz[i];
			if(check(tx,ty,tz))//检验,check函数见上 
			{
				tail++;//符合条件,尾指针++
				 
				q[tail].x=tx;
				q[tail].y=ty;
				q[tail].z=tz;
				q[tail].step=q[head].step+1;//新的点入队 
				
				a[tx][ty][tz]=1;//地图标记 
				
				if(tx==x2 && ty==y2 && tz==z2)//检验是否为终点 
				{
					printf("Escaped in %d minute(s).\n",q[tail].step);
					flag=0;//存在答案,flag=false 
				}
			}
		}
		head++;//头指针++,切记切记,初学者很容易忘记导致死循环 
	}
	if(flag)
		printf("Trapped!\n");//无答案输出Trapped!
	return;
}
int main()
{
	int x1,y1,z1,x2,y2,z2;
	while(1)//无限循环处理多组数据 
	{
		memset(a,0,sizeof(a));//地图清零 
		
		cin>>n>>m>>o;//读入终点坐标 
		
		if(n==0 && m==0&& o==0)//循环结束条件 
			break;
		else
		{
			for(int i=1;i<=n;++i)
				for(int j=1;j<=m;++j)
					for(int k=1;k<=o;++k)
					{
						cin>>p;
						if(p=='.')
							a[i][j][k]=0;
						else if(p=='#')
							a[i][j][k]=1;
						else if(p=='S')//记录起点 
						{
							a[i][j][k]=0;
							x1=i;
							y1=j;
							z1=k;
						}
						else if(p=='E')//记录终点 
						{
							a[i][j][k]=0;
							x2=i;
							y2=j;
							z2=k;
						}
					}//读入地图 
			bfs(x1,y1,z1,x2,y2,z2);	//bfs(起点三个坐标,终点三个坐标)		
		}
	}	
	return 0;//完美结束!!! 
}

2.2走迷宫

#include<bits/stdc++.h>
using namespace std;
int a[17][17],s[17][17],n,m,bx,by,ex,ey;
const string c[16]={"0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15"};
//c用来将整数下标对应字符串的转换 
bool flag;//标记是否输出过解 
void dfs(int bx,int by,string ans){
    if(bx==ex&&by==ey){cout<<ans<<endl;flag=1;}//输出解并标记有路可走 
    int d[4][2]={{0,-1},{-1,0},{0,1},{1,0}};//四个方向搜索 
    for(int i=0;i<4;i++){
        int x=bx+d[i][0],y=by+d[i][1];        
        if(a[x][y]==1&&s[x][y]==0){
            s[x][y]=s[bx][by]+1;//深搜 
            dfs(x,y,ans+"->"+"("+c[x]+","+c[y]+")");//将经历过的点串联起来 
            s[x][y]=0;//回溯 
        }
    }
}
int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)cin>>a[i][j];
    cin>>bx>>by>>ex>>ey;s[bx][by]=1;    
    dfs(bx,by,"("+c[bx]+","+c[by]+")");//起点(bx,by) 
    if(!flag)cout<<-1<<endl;    
    return 0;
}

2.3 Knight Moves(洛谷)

#include<queue>
//STL队列,更方便操作,不至于混淆指针
#include<cstdio>
#include<cstring>
using namespace std;
const int N=10;
char a[N],b[N];
int dx[N]={-1,-2,-2,-1,1,2,2,1};
int dy[N]={-2,-1,1,2,2,1,-1,-2};
struct horse{
    int x,y,t;//节点坐标、深度
}st,ed,tmp;
//st起始状态 ed结束状态 tmp临时转换状态
int dis[N][N],vis[N][N];
//dis节点的深度 vis节点的访问情况
inline int bfs()
{
    if(st.x==ed.x&&st.y==ed.y) return 0;
    queue<horse> q1;
    queue<horse> q2;
    vis[st.x][st.y]=1;
    vis[ed.x][ed.y]=2;
    //起始状态访问的节点记为1,结束状态的记为2
    //当某一状态的队列拓展节点时
    //若vis为0,则标记为自己队列的
    //若已被另一状态的队列标记,则意味着出现重合 
    q1.push(st);
    q2.push(ed);
    int fx,fy,xx,yy;
    while(true){
        if(q1.size()<q2.size()){
        //q.size() 该队列中节点的个数
            fx=q1.front().x;
            fy=q1.front().y;
            //当前q1的节点少,取q1的节点
            for(int i=0;i<8;i++){
                xx=fx+dx[i];yy=fy+dy[i];
                if(xx<1||xx>8||yy<1||yy>8) continue;
                if(vis[xx][yy]==0){
                    tmp.t=q1.front().t+1;
                    tmp.x=xx;tmp.y=yy;
                    q1.push(tmp);
                    vis[xx][yy]=1;
                    //没访问过,标记1表示q1
                    dis[xx][yy]=tmp.t;
                }
                else if(vis[xx][yy]==2)
                return dis[xx][yy]+q1.front().t+1;
                //q2曾访问过这,q1目前访问这,即重合
            }q1.pop();
        }
        else{
            fx=q2.front().x;
            fy=q2.front().y;
            //同样的,这里q2的节点少,取q2的队首
            for(int i=0;i<8;i++){
                xx=fx+dx[i];yy=fy+dy[i];
                if(xx<1||xx>8||yy<1||yy>8) continue;
                if(vis[xx][yy]==0){
                    tmp.t=q2.front().t+1;
                    tmp.x=xx;tmp.y=yy;
                    q2.push(tmp);
                    vis[xx][yy]=2;
                    dis[xx][yy]=tmp.t;
                }
                else if(vis[xx][yy]==1)
                return dis[xx][yy]+q2.front().t+1;
            }q2.pop();
        }
    }
}
int main(void)
{
    while(scanf("%s%s",a,b)!=EOF){
    	//"!=EOF"指输入不为空
        st.x=a[0]-'a'+1;st.y=a[1]-'0';
        ed.x=b[0]-'a'+1;ed.y=b[1]-'0';
        st.t=ed.t=0;
        memset(vis,0,sizeof(vis));
        memset(dis,0,sizeof(dis));
        //多组数据需清空数组
        printf("To get from %c%c to %c%c takes %d knight moves.\n",a[0],a[1],b[0],b[1],bfs());
    }
    return 0;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值