本周学习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;
}