第六周总结——深搜与广搜的用法分析

21 篇文章 1 订阅

       这一周主要以练习深度优先搜索与广度优先搜索为主,看了40篇与BFS和DFS有关题目的题解;复习了5篇涉及STL的题解;还有5篇思维题目。对于一个题目如果不告诉涉及深搜或广搜的话,靠自我判断还是不够迅速,与其他相结合使用的熟练度有所提高,但还需加强。下面由以下两个方面对本周学习进行分析:

一、深度优先搜索分析

1、八皇后问题

题意:在8×8格的国际象棋上摆放8个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一

行、同一列或同一斜线上,问有多少种摆法。

#include<iostream>
using namespace std;
int n;
int row[101]={0},column[101]={0},l_diagonal[101]={0},r_diagonal[101]={0};
int a[101];//记录是否可以放置皇后
int sum=0;//记录可行方案数
void dfs(int x);
int main()
{
    cin>>n;
    dfs(1);
    cout<<sum<<endl;
    return 0;
}
void dfs(int x)
{
    int y;
    int i;

    if(x==n+1)//如果到达终点,说明方案成功
    {
        sum++;
    }
    for(y=1;y<=n;y++)
    {
        if(!row[x]&&!column[y]&&!l_diagonal[x-y+n]&&!r_diagonal[x+y])//如果行、列、左对角线、右对角线均可以下
        {
            a[x]=y;//说明该点可以放置一皇后,记录

            row[x]=1;
            column[y]=1;
            l_diagonal[x-y+n]=1;
            r_diagonal[x+y]=1;
            dfs(x+1);
            row[x]=0;
            column[y]=0;
            l_diagonal[x-y+n]=0;
            r_diagonal[x+y]=0;
        }
    }
}

分析:

       这个问题如果用八个for循环,不仅麻烦而且很容易写错,尝试一下用深度优先搜索解答。

        把棋盘作为一个坐标图,首先认为x轴为深度标识(平行层),y轴为深度,从第一层开始逐层往下深搜,然后遍历y轴,利用数组标记这一行这一列这一条左右对角线是否被深搜过了。当某一列出现了不满足题意的情况,我们就进行下一列。如果所有列都不满足条件,则代表上一层的有问题,我们需要回到前一层。当放置第一层的皇后,进入下一层然后先对上一层检查,若满足题意,则进行下一行的放置。反之则将不满足的皇后移位。

2、财产分配问题  AOJ 0118:Property Distribution

题意:一块矩形的区域,里面有三种字符,分别为@#*,把相邻的相同字符连接成整体,问可以分成多少块。

部分代码:

char tmap[1000][1000];
 
int w,h;
 
int ans;
 
int dfs(int x,int y,char seed){
	if(x<0 || x>w || y>h || y<0) return 0;
	if(tmap[x][y]==0 ||seed != tmap[x][y]) return 0;
	tmap[x][y] = 0;
			dfs(x,y-1,seed);
		dfs(x-1,y,seed);dfs(x+1,y,seed);
			dfs(x,y+1,seed);
	
	return 1;
}
 

分析:

       这个题归根结底运用深度优先搜索。首先可以把每一个字符假设为一个坐标点,方便标记。要求分配区域的数量,一个字符的上下左右的字符如果有相同的可以算为同一个区域,把搜索过或合并的位置变为另一字符,将相同的归为1片,数一下总共有多少片便可。

总结:

       深度优先搜索,可以解决迷宫问题、八数码/华容道问题、最远路径问题(不太常用)、传授问题等。因为深度优先搜索按照某种条件往前试探搜索,如果前进中遭到失败(正如遇到死胡同)则退回头另选通路继续搜索,直到找到满足条件的目标为止。

二、广度优先搜索

1、序列反转

题目:给定序列1 2 3 4 5 6,再给定一个k.对于序列,我们可以将其中k个连续的数全部反转过来,例如k = 3的时候,上述序列经过1步操作后可以变成:3 2 1 4 5 6 ,如果再对序列 3 2 1 4 5 6进行一步操作,可以变成3 4 1 2 5 6.给定初始序列,以及结束序列,以及k的值,求出从初始序列到结束序列的转变至少需要几步操作?

分析:

       本题已经给定初始序列跟最终序列,要求求出中间的最少操作,用BFS。可以把每一个数当做一个节点,一层一层尝试,对于每一次的操作就相当于从每个节点产生一条路线,首先反转到最终序列的节点所延伸的层数,便是最终结果。

假设起始节点是:{1 2 3 4 5 6},终点是:{3 4 1 2 5 6}  k=3

首先设有4个节点:1、2、3、4;每个节点经过一次反转得到:

{3 2 1 4 5 6}、{1 4 3 2 5 6}、{1 2 5 4 3 6}、{1 2 3 6 5 4}

然后继续搜索即可得到结果,此时的操作数就是所得结果2。

2、填涂颜色     P1162 填涂颜色 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题意:由数字0组成的方阵中,有一任意形状闭合圈,闭合圈由数字1构成,围圈时只走上下左右4个方向。要求把闭合圈内的所有空间都填写成2.

#include<bits/stdc++.h>
using namespace std;
#define maxn 35
int a[maxn][maxn],n;
int dx[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
void bfs(int x,int y){
	a[x][y]=0;
	for(int i=0;i<4;i++){
		int ux = x+dx[i][0],uy = y+dx[i][1];
		if(ux>=0 and ux<=n+1 and uy>=0 and uy<=n+1 and a[ux][uy]==2){
			bfs(ux,uy);
		}
	}
}
int main(){
	for(int i=0;i<35;i++)for(int j=0;j<35;j++)a[i][j]=2;
	cin>>n;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			int tmd;
			cin>>tmd;
			if(tmd==1)a[i][j]=1;
		}
	}
	bfs(0,0);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			cout<<a[i][j]<<" ";
		}
		if(i!=n)cout<<endl;
	}
	return 0;
}

分析:

       看到此题之后第一反应是寻找1,然后找到圈内的点,使其变为2,但会发现这不容易实现,而且非常复杂,换另一种思路:找圈外的点。首先我们可以把整个方阵全部变为数字2,接下来把1的地方还原回1,之后找1圈外的点,把圈外的2变成0,大体思路就是这样。用bfs标记闭合圈外的2,从边界开始如果一个数为2且没有被搜索过,就开始搜索它周围的并且在边界外的2,最后把标记的圈外的2变为0.

总结:

        针对BFS,解决最多的问题就是最优解与最短路径问题以及走迷宫、最少操作等问题,广度搜索遍历是一层一层的去搜索从入口到出口的过程,所以在找到出口的时候,深度不一定是最深,但一定是当前最短路径(亦或是最优解)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值