迷宫
题目描述
给定一个 N \times MN×M 方格的迷宫,迷宫里有 TT 处障碍,障碍处不可通过。
在迷宫中移动有上下左右四种方式,每次只能移动一个方格。数据保证起点上没有障碍。
给定起点坐标和终点坐标,每个方格最多经过一次,问有多少种从起点坐标到终点坐标的方案。
输入格式
第一行为三个正整数 N,M,TN,M,T,分别表示迷宫的长宽和障碍总数。
第二行为四个正整数 SX,SY,FX,FYSX,SY,FX,FY,SX,SYSX,SY 代表起点坐标,FX,FYFX,FY 代表终点坐标。
接下来 TT 行,每行两个正整数,表示障碍点的坐标。
输出格式
输出从起点坐标到终点坐标的方案总数。
输入输出样例
输入 #1复制
2 2 1 1 1 2 2 1 2输出 #1复制
1说明/提示
对于 100\%100% 的数据,1 \le N,M \le 51≤N,M≤5,1 \le T \le 101≤T≤10,1 \le SX,FX \le n1≤SX,FX≤n,1 \le SY,FY \le m1≤SY,FY≤m。
1.该题使用深度优先搜索来实现,从起始坐标往下走,每一次一共有四个方位可以考虑,我采用了顺时针的走法,如果向右走不通则向下,向下不通则向左,向左也不通就向上,用一个二维数组来记住每一边走位的偏差值。
2.每一次都要判断是否已经走到了终点,到了计数器num++,否则返回重新找出下一步可以走的位置。
3.判断下一步走的位置是否为边界,是边界的话则采取下一种走法。
4.如果走的位置没有障碍物并且这一步还没有走过,则走到这里来,用数组b标记已走,递归,走下一步,这一步走完后取消这里的标记。
#include"stdio.h"
int a[10][10],b[10][10],c[10][2],n,m,fx,fy,sx,sy,num=0;
void fun(int x,int y,int step)
{
int next[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
int tx,ty,k;
if(x==fx&&y==fy)
{
num++;
return;
}
for(k=0;k<=3;k++)
{
tx=x+next[k][0];
ty=y+next[k][1];
if(tx<1||tx>n||ty<1||ty>m) continue;
if(a[tx][ty]==0&&b[tx][ty]==0)
{
b[tx][ty]=1;
fun(tx,ty,step+1);
b[tx][ty]=0;
}
}
}
main()
{
int t,i,j;
scanf("%d %d %d",&n,&m,&t);
scanf("%d %d %d %d",&sx,&sy,&fx,&fy);
for(i=0;i<t;i++)
scanf("%d %d",&c[i][0],&c[i][1]);
for(i=0;i<t;i++)
a[c[i][0]][c[i][1]]=1;
b[sx][sy]=1;
fun(sx,sy,0);
printf("%d",num);
}
填涂颜色
题目描述
由数字 00 组成的方阵中,有一任意形状闭合圈,闭合圈由数字 11 构成,围圈时只走上下左右 44 个方向。现要求把闭合圈内的所有空间都填写成 22。例如:6\times 66×6 的方阵(n=6n=6),涂色前和涂色后的方阵如下:
0 0 0 0 0 0 0 0 1 1 1 1 0 1 1 0 0 1 1 1 0 0 0 1 1 0 0 0 0 1 1 1 1 1 1 1
0 0 0 0 0 0 0 0 1 1 1 1 0 1 1 2 2 1 1 1 2 2 2 1 1 2 2 2 2 1 1 1 1 1 1 1
输入格式
每组测试数据第一行一个整数 n(1 \le n \le 30)n(1≤n≤30)。
接下来 nn 行,由 00 和 11 组成的 n \times nn×n 的方阵。
方阵内只有一个闭合圈,圈内至少有一个 00。
//感谢黄小U饮品指出本题数据和数据格式不一样. 已修改(输入格式)
输出格式
已经填好数字 22 的完整方阵。
输入输出样例
输入 #1复制
6 0 0 0 0 0 0 0 0 1 1 1 1 0 1 1 0 0 1 1 1 0 0 0 1 1 0 0 0 0 1 1 1 1 1 1 1输出 #1复制
0 0 0 0 0 0 0 0 1 1 1 1 0 1 1 2 2 1 1 1 2 2 2 1 1 2 2 2 2 1 1 1 1 1 1 1说明/提示
对于 100\%100% 的数据,1 \le n \le 301≤n≤30。
- 这个题仿照上面那个题写,用next数组存每一种下一步走法的偏差值。
- 主函数中下标i和j都从1开始,目的是将外圈用0包围以此方便使用联通块的方法将不在闭合圈内的0(1围成的闭合圈外)全部赋值为0。因为题目要求我们只需要将闭合圈内的0赋值为2,但是我们反之将闭合圈外的0赋值为2因为这样在进行搜索时更加方便,在输出时判断一下就可以正确输出了。
#include"stdio.h"
int n,a[50][50];
void fun(int x,int y)
{
int next[4][2]={{1,0},{0,-1},{-1,0},{0,1}};
int i,j,k,zx,zy;
a[x][y]=2;
for(i=0;i<4;i++)
{
zx=x+next[i][0];
zy=y+next[i][1];
if(zx>=0&&zx<=n+1&&zy>=0&&zy<=n+1&&a[zx][zy]==0)
fun(zx,zy);
}
}
main()
{
int i,j;
scanf("%d",&n);
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
scanf("%d",&a[i][j]);
fun(0,0);
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
if(a[i][j]==2) printf("0 ");
else if(a[i][j]==0) printf("2 ");
else printf("1 ");
printf("\n");
}
}
Lake Counting S
题意翻译
由于近期的降雨,雨水汇集在农民约翰的田地不同的地方。我们用一个 N\times M(1\leq N\leq 100, 1\leq M\leq 100)N×M(1≤N≤100,1≤M≤100) 的网格图表示。每个网格中有水(
W
) 或是旱地(.
)。一个网格与其周围的八个网格相连,而一组相连的网格视为一个水坑。约翰想弄清楚他的田地已经形成了多少水坑。给出约翰田地的示意图,确定当中有多少水坑。输入第 11 行:两个空格隔开的整数:NN 和 MM。
第 22 行到第 N+1N+1 行:每行 MM 个字符,每个字符是
W
或.
,它们表示网格图中的一排。字符之间没有空格。输出一行,表示水坑的数量。
输入输出样例
输入 #1复制
10 12 W........WW. .WWW.....WWW ....WW...WW. .........WW. .........W.. ..W......W.. .W.W.....WW. W.W.W.....W. .W.W......W. ..W.......W.输出 #1复制
3说明/提示
OUTPUT DETAILS: There are three ponds: one in the upper left, one in the lower left, and one along the right side.
- 该题我采用了和上面那个题相同的想法,找出一个个的联通块。
- 此题有八个方向需要考虑,同样的,将他们每个方向的偏移量用数组next存下来。
- 在主函数里找到每个W联通块的开头,然后调用函数进行搜索,计数器num加一。
- 将每走一步找到的W赋值为.方便下一个联通块的搜索。
#include"stdio.h" int n,m,num=0; char a[110][110]; void fun(int x,int y) { int i,zx,zy; int next[8][2]={{1,0},{1,1},{0,1},{1,-1},{-1,0},{-1,-1},{0,-1},{-1,1}}; a[x][y]='.'; for(i=0;i<8;i++) { zx=x+next[i][0]; zy=y+next[i][1]; if(zx>=0&&zx<n&&zy>=0&&zy<m&&a[zx][zy]=='W') fun(zx,zy); } } main() { int i,j; scanf("%d %d",&n,&m); for(i=0;i<n;i++) { for(j=0;j<m;j++) scanf("%c",&a[i][j]); getchar(); } for(i=0;i<n;i++) { for(j=0;j<m;j++) if(a[i][j]=='W') { fun(i,j); num++; } } printf("%d",num); }
小结
- 今天主要主要学习了深度搜索,了解到很多复杂问题都使用到了dfs来解决。
- 此类题目熟练度不够,还需要多加练习。
- 同时了解到了dfs代码的一般套路。
void dfs(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
dfs(路径,选择列表); // 递归
回溯,撤销处理结果
}
}