dfs算法搜索(详细)

目录

算法简介:

枚举方式:

1.每一个数都有两种状态,也就是选或不选,时间复杂度也就是2^n,每一个数都有选和不选两种状态。

 2.生成给定集合所有可能排列的方法,与之不同的是同样是1 2 3三个数字,1,2, 3和 1,3,2是两种方案。

3.不考虑元素的顺序。也就是 1 2 3 和 1 3 2是同一种方案

图的搜索:

 奶牛和草丛

题目描述

输入

输出

样例输入 

样例输出 

解题步骤:

解题代码:

练习:

洛谷P1219 [USACO1.5] 八皇后 Checker Challenge

题目描述

输入格式

输出格式

留下你的足迹吧!谢谢。


算法简介:

DFS算法的基本思想是从图中的某个顶点v出发,访问此顶点,然后依次从v的未被访问的邻接点出发深度优先遍历图,直至图中所有和v有路径相通的顶点都被访问到。若此时图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点,重复上述过程,整个进程反复进行直到所有顶点都被访问为止。

枚举方式:

1.每一个数都有两种状态,也就是选或不选,时间复杂度也就是2^n,每一个数都有选和不选两种状态。

       代码实现:

#include<bits/stdc++.h>
using namespace std;
int n;
int vis[20];
void dfs(int x){
    if(x>n){
        for(int i=1;i<=n;i++){
            if(vis[i]==1)
            cout<<i<<" ";
        }
        cout<<'\n';
        return;
    }
    vis[x]=-1;
    dfs(x+1);
    vis[x]=0;
    vis[x]=1;
    dfs(x+1);
    vis[x]=0;
}
int main(){
    cin>>n;
    dfs(1);
    return 0;
}

 2.生成给定集合所有可能排列的方法,与之不同的是同样是1 2 3三个数字,1,2, 3和 1,3,2是两种方案。

        代码实现:

 for(int i=1;i<=n;i++){
        if(!vis[i]){
            vis[i]=1;//选过标记
            a[x]=i;//表示被选
            dfs(x+1);//继续选下一个
            vis[i]=0;//回溯重置
            a[x]=0;
        }
    }

3.不考虑元素的顺序。也就是 1 2 3 和 1 3 2是同一种方案

        代码实现:

  for(int i=u;i<=n;i++){
        a[x]=i;
        dfs(x+1,i+1);//选下一个数字,从i+1开始往后选
        a[x]=0;
    }

图的搜索:

以下题举例:

 奶牛和草丛

        

题目描述

奶牛Bessie计划好好享受柔软的春季新草。新草分布在R行C列的牧场里。它想计算一下牧场中的草丛数量。
在牧场地图中,每个草丛要么是单个“#”,要么是有公共边的相邻多个“#”。给定牧场地图,计算有多少个草丛。
例如,考虑如下5行6列的牧场地图
.#....
..#...
..#..#
....##
.....#
这个牧场有3个草丛:一个在第一行,一个在第二列横跨了二、三行,一个在第三行横跨了三、四、五行。

输入

第一行包含两个整数R和C,中间用单个空格隔开。
接下来R行,每行C个字符,描述牧场地图。字符只有“#”或“.”两种。(1 <= R, C <= 100 )

输出

输出一个整数,表示草丛数。

样例输入 
5 6
.#....
..#...
..#..#
....##
.....#
样例输出 
3

这是一题典型的dfs,

解题步骤:


1.选择起始点:从图的某个顶点u开始。
2.标记当前顶点:将当前顶点u标记为已访问,以避免重复访问。
3.遍历邻接点:对于u的每个未访问的邻接点v,递归地执行dfs,从v开始。
4.回溯:当没有更多的邻接点可以遍历时,返回到上一步的顶点。 

解题代码:

#include<bits/stdc++.h>
using namespace std;
int m,n,c;
char a[105][105];
int b[105][105];
void f(int x,int y){
    if(x>=1&&x<=m&&y>=1&&y<=n){
        if(a[x][y]=='#'&&b[x][y]==0){//是草丛,且没被遍历
            b[x][y]=1;
            //搜四个方向
            f(x+1,y);
            f(x-1,y);
            f(x,y+1);
            f(x,y-1);
        }
    }
    else return;//不符条件,回溯。
}
int main(){
    cin>>m>>n;
    //输入
    for(int i=1;i<=m;i++){
        for(int j=1;j<=n;j++){
            cin>>a[i][j];
        }
    }
    //遍历查找
    for(int i=1;i<=m;i++){
        for(int j=1;j<=n;j++){
            if(a[i][j]=='#'&&b[i][j]==0){
                c++;
                f(i,j);
            }
             
        }
    }
    cout<<c;
    return 0;
} 

练习:

洛谷P1219 [USACO1.5] 八皇后 Checker Challenge

题目描述

一个如下的 6×66×6 的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。

上面的布局可以用序列 2 4 6 1 3 5 来描述,第 i 个数字表示在第 i 行的相应位置有一个棋子,如下:

行号 1 2 3 4 5 6

列号 2 4 6 1 3 5

这只是棋子放置的一个解。请编一个程序找出所有棋子放置的解。
并把它们以上面的序列方法输出,解按字典顺序排列。
请输出前 3 个解。最后一行是解的总个数。

输入格式

一行一个正整数 n,表示棋盘是 n×n 大小的。

输出格式

前三行为前三个解,每个解的两个数字之间用一个空格隔开。第四行只有一个数字,表示解的总数。

#include<bits/stdc++.h>
using namespace std;
int n,vx[15],vy[15],v1[30],v2[30],a[15],c;
void f(int i){
	if(i>n){
		c++;
		if(c<=3){
			for(int k=1;k<=n;k++)cout<<a[k]<<" ";
			cout<<endl;
		}
		return;
	}
	for(int j=1;j<=n;j++){
		if(!vx[i]&&!vy[j]&&!v1[i-j+n]&&!v2[i+j]){
			a[i]=j;
			vx[i]=1;
			vy[j]=1;
			v1[i-j+n]=1;
			v2[i+j]=1;
			f(i+1);
			vx[i]=0;
			vy[j]=0;
			v1[i-j+n]=0;
			v2[i+j]=0;
		}
	}
} 
int main() {
    cin>>n;
    f(1);
    cout<<c;
	return 0;
}

留下你的足迹吧!谢谢。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值