dfs(接着学)

dfs(接着学)_第1张图片

题目如上

这是一道经典dfs的题目,和我之前写的那道题目有点相似。我们继续一点一点构架架构,再在这些架构里面补充细节。

首先从简单的主函数构建起

由于有些数需要在dfs函数中使用,所以我们将这个变量设置为全局变量。再在外面设置一个方向数组

#include
int a[6][6]={0};//设置迷宫,并且为其初始化为0
int book[6][6]={0};//设置标记数组
int dx[4]={0,0,1,-1};
int dy[4]={-1,1,0,0};
int sx,sy;//开始的位置
int fx,fy;//结束的位置
int t;//设置障碍的次数
int l,r;//障碍在二维数组中的具体位置
int n,m;//这个迷宫的大小
int main()
{
	scanf("%d %d %d",&n,&m,&t);
	scanf("%d %d %d %d",&sx,&sy,&fx,&fy);
    a[sx][sy]=1;//这一步很重要,要将起始的位置设置为1,代表它已经走过了,不然有些数据会错,这个还一个大牛提醒我的
	for(int i=1;i

然后再是dfs的模板

void dfs(int step)
{
	if(终止条件)
	{
		最后需要进行的语句,有的时候自己找最大值最小值,有些时候求走了多少步
		return ; 一定要加return 否则就会一种进行下去
	}
	for()对每一种情况进行判断
	{
		if(走没有别标记的点){
		book[step]=1;标记已经来过了
		dfs(step+1);这个是进行下一步,不装男墙不回头的特性
		book[step]=0;回溯
	  } 
	}
	return ;
}

我在这个模板里面逐渐补充细节,这个题目只不过是将一位数组转换为二维数组

终止条件很容易想到就是dfs里面定义的x,y移动到终点坐标即可

根据题目要求在函数外面定义一个sum=0,用于记录步数这样if语句我们就完成了

然后再是for循环我们将每一种移动的情况都要考虑,所以只要我们将方向数组进行一次循环就可以将每一种情况都考虑进去。这个没有被标记的点就是book的这个坐标为0的意思,然后不能是障碍物,所以a也要为0。

同时我们还要考虑是否越界的问题,如果越界就进入下一次循环

dfs函数代码如下

#include
int a[6][6]={0};//设置迷宫,并且为其初始化为0
int book[6][6]={0};//设置标记数组
int dx[4]={0,0,1,-1};
int dy[4]={-1,1,0,0};
int sx,sy;//开始的位置
int fx,fy;//结束的位置
int t;//设置障碍的次数
int l,r;//障碍在二维数组中的具体位置
int n,m;//这个迷宫的大小
int sum=0;
void dfs(int x,int y)
{
    if(x==fx&&y==fy)
    {
        sum++;
        return ;
	}
	int tx,ty;
	for(int i=0;i<4;i++)
	{
		tx=x+dx[i];
		ty=y+dy[i];
		if(tx>n||tx<1||ty>m||ty<1)
		continue;
		if(book[tx][ty]==0&&a[tx][ty]==0)
		{
			book[tx][ty]=1;
			dfs(tx,ty);
			book[tx][ty]=0;
		}
	}
	return ;
}
int main()
{
	scanf("%d %d %d",&n,&m,&t);
	scanf("%d %d %d %d",&sx,&sy,&fx,&fy);
	for(int i=0;i

dfs(接着学)_第2张图片

这个题目真的是让我想破脑袋都找不问题在哪里,是一个很细节的地方而且很简单。

首先来说一下思路这题可以dfs,也可以bfs来写,我还没学bfs所以就来写dfs

可以发现这种图的遍历(一般是二维数组),往往都要设置一个方向移动数组(其中题目要求也有这个)这个题目意思也算比较难理解,先讲一下题目的意思:W代表着水,.代表这旱地回想一下我们平常看到的水坑,一滩水是不是被一圈地围着的?那有人会问了,这一滩W中怎么会有.?这个怎么算是否为一滩水呢?其实只要是以W为中心,上,上左,上右,下左,下右,左,右只要有一个为W,那么这些相邻的W就可以构成一滩水。所以我们用肉眼来看,可以直观的看出右三滩水。

接下来讲一下思路和结构。

思路:用一个双重循环来遍历二维字符数组中的每一个字符若遇到W,则代表会有水滩,有W就有水滩。我们可以用深度搜素的特性,一路搜到底(为了方便理解,我将双重循环正在遍历的W成为W1,而它相邻的W称为W2)。我们一旦发现有W2,就将W1赋值为'.',深搜的特性会将W及其相邻的相邻......的W都赋值为'.'。这样这一摊水就全部变成旱地了,当双重循环遍历到这(这里如果原本有水)但由于之前的深搜将这变成旱地,所以不会重复计算

结构:主函数一个双重循环(用于遍历整个二维字符数组),dfs函数中一个循环(用于将每一个方向都遍历),和一个dfs的递归(进行深度(把这个深度想象成钻地的那种深度,不到地心不回头,不撞南墙不回头)探索)。

任然是由部分结构到整体,再补充细节

主函数

#include
int n,m;//田地的大小
char a[110][100]//题目要求
int sum=0;
int main()
{
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		scanf(" %c",&a[i][j]);//这里是一个巨坑呀!!!我TM是真想不到%c前面还要加一个空格,这样的输入才是对的
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			if(a[i][j]=='W')//发现了水坑
			{
				sum++;//发现了水坑就会有一滩水,不用担心双重循环会重复检查到W,深搜会将相邻的都变成旱地的
				dfs(i,j);
			}
		}
	}
	printf("%d",sum);;//打印答案
	return 0;
}

dfs函数

void dfs(int x,int y)
{
	a[x][y]='.';
	int dx[8]={ 1, 1, 1, 0, 0,-1,-1,-1};//x方向
	int dy[8]={-1, 0, 1, -1,1,-1, 0, 1};//y方向
    for(int i=0;i<8;i++)
    {
    	int tx=x+dx[i];
    	int ty=y+dy[i];//你们发现没有,这种关于兔的遍历的题目往往需要三个参数x,tx,dx(y也同理嗷)分别为当前的位置,相邻(也可以理解为之后)的位置,由x->tx的数值
    	if(tx>=1&&tx<=n&&ty>=1&&ty<=m&&a[tx][ty]=='W')
    	{
    		a[tx][ty]='.';//将相邻的W改成'.'
    		dfs(tx,ty);
		}
	}
}

完整代码

#include
int n,m;//田地的大小
char a[110][100];//题目要求
int sum=0;
void dfs(int x,int y)
{
	a[x][y]='.';
	int dx[8]={ 1, 1, 1, 0, 0,-1,-1,-1};//x方向
	int dy[8]={-1, 0, 1, -1,1,-1, 0, 1};//y方向
    for(int i=0;i<8;i++)
    {
    	int tx=x+dx[i];
    	int ty=y+dy[i];//你们发现没有,这种关于兔的遍历的题目往往需要三个参数x,tx,dx(y也同理嗷)分别为当前的位置,相邻(也可以理解为之后)的位置,由x->tx的数值
    	if(tx>=1&&tx<=n&&ty>=1&&ty<=m&&a[tx][ty]=='W')
    	{
    		a[tx][ty]='.';//将相邻的W改成'.'
    		dfs(tx,ty);
		}
	}
}
int main()
{
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		scanf(" %c",&a[i][j]);//这里是一个巨坑呀!!!我TM是真想不到%c前面还要加一个空格,这样的输入才是对的
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			if(a[i][j]=='W')//发现了水坑
			{
				sum++;//发现了水坑就会有一滩水,不用担心双重循环会重复检查到W,深搜会将相邻的都变成旱地的
				dfs(i,j);
			}
		}
	}
	printf("%d",sum);;//打印答案
	return 0;
}

dfs(接着学)_第3张图片

我感觉这题和之前发的那个取数游戏难度差不多大,都搞的我汗流浃背

首先就是怎样从这个题目中抽象出一个dfs。我们先从第一行开始放一个,接下整个棋盘都会因为这一个数而影响,行的,列的,对角线的。如果我们每次放一个数,就将对应的行,列,对角线进行标记,然后移动到下一个位置的时候在进行判断(如果没有标记,就递归,然后把这个位置标记),直到最后一个,看最后一个是否满足终止条件(即step是否等于n)。这不就是一个dfs的模型吗?

我任然按照之前,先从每一函数开始构建出大体框架,再补充细节

首先是主函数:这箱单简单输入n,再dfs,再输出sum的值

int main()
{
    scanf("%d",&n);
    dfs(1);
    printf("%d\n",sum);
    return 0;
}

接下来再是dfs函数。这里有一个非常巧妙的地方,不需要设置二维数组,而是用四个一维数组分别代表行,列,双对角线。优点在于一维只要进行一次赋值,不需要像二维数组那样用for将每一行,每一列,每一个对焦线进行赋值(特别是对角线,还需要二维数组来标记很麻烦还容易错)而这个dfs函数的变量就是每一行。每一列作为随机值用for来表示。按照前面题的模板带进去

void dfs(int step)
{
    if(step>n)//
    {
        print();
        return ;
    }
    else{
    for(int j=1;j<=n;j++)//这个循环是将j作为列
    {
        if(b[j]==0&&c[step+j]==0&&d[step-j+n]==0)
        {
            a[step]=j;//将没有被标记的点作为行赋值
            b[j]=1;//列标记
            c[step+j]=1;//双对角线标记
            d[step-j+n]=1;//
            dfs(step+1);
            b[j]=0;//回溯
            c[step+j]=0;
            d[step-j+n]=0;
        }
    }
  }
}

输出函数:如果小于3,就进行输出,同时还需要进行sum的累积

void print()
{
    if(sum<=2)//
    {
        for(int i=1;i<=n;i++)
            printf("%d ",a[i]);
        printf("\n");
    }
    sum++;
}

完整代码

#include
int n;
int a[100]={0};
int b[100]={0};
int c[100]={0};
int d[100]={0};
int sum=0;
void print()
{
    if(sum<=2)//
    {
        for(int i=1;i<=n;i++)
            printf("%d ",a[i]);
        printf("\n");
    }
    sum++;
}
void dfs(int step)
{
    if(step>n)//
    {
        print();
        return ;
    }
    else{
    for(int j=1;j<=n;j++)
    {
        if(b[j]==0&&c[step+j]==0&&d[step-j+n]==0)
        {
            a[step]=j;
            b[j]=1;
            c[step+j]=1;
            d[step-j+n]=1;
            dfs(step+1);
            b[j]=0;
            c[step+j]=0;
            d[step-j+n]=0;
        }
    }
  }
}
int main()
{
    scanf("%d",&n);
    dfs(1);
    printf("%d\n",sum);
    return 0;
}

你可能感兴趣的:(深度优先,算法)