第七周的题

本周学习DFS入门

1、

题目:洛谷       B3625 迷宫寻路

机器猫被困在一个矩形迷宫里,迷宫可以视为一个 n×m 矩阵,每个位置要么是空地,要么是墙。机器猫只能从一个空地走到其上、下、左、右的空地。机器猫初始时位于(1,1) 的位置,问能否走到 (n,m) 位置。输入:第一行,两个正整数 n,m。接下来 n 行,输入这个迷宫。每行输入一个长为 m 的字符串,# 表示墙,. 表示空地。输出:仅一行,一个字符串。如果机器猫能走到 (n,m),则输出 Yes;否则输出 No

思路:

典型的迷宫问题,用dfs一条路搜索,到达终点直接结束并输出,无路可走时回溯,使用vis数组标记是否已经访问过,回溯时让那一步的vis重新变成0,从而完成回溯

# include <bits/stdc++.h>
using namespace std;
int m,n;
char s[105][105];
int vis[105][105];
bool dfs(int x1,int y1){
	vis[x1][y1]=1;
	if(x1==m-1&&y1==n-1) return true;
	int a[4]={-1,0,1,0},b[4]={0,-1,0,1};
	for(int i=0;i<4;i++)
	{
		if(x1+a[i]>=0&&x1+a[i]<m&&y1+b[i]>=0&&y1+b[i]<n&&vis[x1+a[i]][y1+b[i]]==-1)
		{
			if(dfs(x1+a[i],y1+b[i])) return true;
		}
	}
	vis[x1][y1]=1;
	return false;
}
int main()
{
	memset(vis,-1,sizeof(vis));
	scanf("%d %d",&m,&n);
	for(int a=0;a<m;a++)
	for(int b=0;b<n;b++)
	{
		scanf(" %c",&s[a][b]);
		if(s[a][b]=='.') vis[a][b]=-1;
		else if(s[a][b]=='#') vis[a][b]=0;
	}
	if(s[m-1][n-1]=='#') 
	{
		printf("No");
		return 0;
	}
	else {
		if(vis[0][0]==0||vis[m-1][n-1]==0) printf("No");
		else if(dfs(0,0)) printf("Yes");
		else printf("No");
	}
	return 0;
}

2、

题目:洛谷     P1706 全排列问题

按照字典序输出自然数 1 到 n 所有不重复的排列,即 n 的全排列,要求所产生的任一数字序列中不允许出现重复的数字。输入:一个整数 n。输出:由 1∼n 组成的所有不重复的数字序列,每行一个序列。每个数字保留 5 个场宽。

思路:

全排列问题,用dfs,dfs函数括号里的step,表示每一次排列里的数字个数,step为n+1时,输出这一串排列,并进行回溯,重新再排列,直到全部排列完,就不再回溯

# include <bits/stdc++.h> 
using namespace std;
int n;
int num[10],vis[10]; 
void dfs (int step);

int main()
{
while (scanf("%d",&n )==1)
{
memset (vis,0,sizeof(vis));
dfs(1);
}
return 0;
}

void dfs(int step){ 
	if(step==n+1){
		for(int i=1;i<=n;i++)
			printf("%5d",num[i]);
		printf("\n");
		return;
	}
	for(int i=1;i<=n;i++)
	{
		if(vis[i]==0){
			num[step]=i;
			vis[i]=1;
			dfs(step+1);
			vis[i]=0;
			
		}
	} 
}

3、

题目:洛谷     P1451 求细胞数量

题目描述

一矩形阵列由数字 0 到 9 组成,数字 1 到 9 代表细胞,细胞的定义为沿细胞数字上下左右若还是细胞数字则为同一细胞,求给定矩形阵列的细胞个数。输入第一行两个整数代表矩阵小 n 和 m。接下来 n 行,每行一个长度为 m 的只含字符 0 到 9 的字符串,代表这个 n×m 的矩阵。输出一行一个整数代表细胞个数。

思路:

dfs的思路和迷宫类似,都有四种搜索状态,向上,向右,向下,向左,不过这次不用回溯,因为这道题不用一直搜索到头,这道题进行dfs时,在某个细胞搜索完毕后,vis=1,说明已访问,然后main函数中for循环从0,0到m,n,当没被访问过且不为0时,就dfs搜索访问,访问之后,同一个细胞的都会变为已访问过,然后即可统计有几个细胞

# include <bits/stdc++.h>
using namespace std;
int m,n,ans;
int s[105][105];
int vis[105][105];
int a[4]={1,0,-1,0};
int b[4]={0,1,0,-1} ;
void dfs(int x,int y)
{
	vis[x][y]=1;
	for(int i=0;i<4;i++)
	{
		int nx=x+a[i];
		int ny=y+b[i];
		if(s[nx][ny]==0||vis[nx][ny]==1) continue;
		dfs(nx,ny);
	}
}
int main()
{
	memset(vis,0,sizeof(vis));
	memset(s,0,sizeof(s));
	scanf("%d %d",&m,&n);
	for(int i=0;i<m;i++)
	for(int j=0;j<n;j++)
	{
		scanf("%1d",&s[i][j]);
	}
	for(int i=0;i<m;i++)
	for(int j=0;j<n;j++)
	{
		if(s[i][j]!=0&&vis[i][j]==0)
		{
			dfs(i,j);
			ans++;
		}
	}
	printf("%d",ans);
	return 0;
} 

4、

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

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

上面的布局可以用序列 2 4 6 1 3 52 4 6 1 3 5 来描述,第 i 个数字表示在第 i 行的相应位置有一个棋子,如下:行号 1 2 3 4 5 61 2 3 4 5 6    列号 2 4 6 1 3 52 4 6 1 3 5这只是棋子放置的一个解。请编一个程序找出所有棋子放置的解。并把它们以上面的序列方法输出,解按字典顺序排列。请输出前 3 个解。最后一行是解的总个数。输入:一行一个正整数 n,表示棋盘是 n×n 大小的。输出:前三行为前三个解,每个解的两个数字之间用一个空格隔开。第四行只有一个数字,表示解的总数。

思路:

数组a【i】表示第i行的女王应该放的列数,数组b表示某一列能不能放女王,数组c表示左下到右上的某一条对角线能不能放女王,数组d表示左上到右下的某一条对角线能不能放女王。用dfs(),判断时判断数组b、c、d是否为0,(数组a是行数,一行一行来的,不需要考虑),如果满足,a【i】数组等于当前循环的那一列,然后标记已访问,然后进行dfs(下一行),然后回溯(标记为未访问),main函数里面从1开始

# include <bits/stdc++.h>
using namespace std;
int n;
int a[15]={0},b[15]={0},c[30]={0},d[30]={0};
int num=0;
void printf()
{
	if(num<=2)
	for(int i=1;i<=n;i++)
	{
		printf("%d ",a[i]);
		if(i==n)  printf("\n");
	}
	
	num++;
}
void dfs(int rank)  //rank表示第rank行 
{
	if(rank>n) 
	{
		printf();
		return;
	}
	for(int j=1;j<=n;j++)
	{
		if(b[j]==0&&c[rank+j-1]==0&&d[j+n-rank]==0)
		{
		a[rank]=j;
		b[j]=1;
		c[rank+j-1]=1;
		d[j+n-rank]=1;
		dfs(rank+1);
		b[j]=0;
		c[rank+j-1]=0;
		d[j+n-rank]=0;
	    }
	}
}
int main()
{
	scanf("%d",&n);
	dfs(1);
	printf("%d",num);
	return 0;
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值