对于搜索题目的实践
深度搜索与广度搜索的区别以及使用范围
迷宫
题目描述
给定一个 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。
主要思路
对于障碍的题目,一般想法使用搜索,此题发现使用dfs或是bfs都是可以的,然后注意定义一个数组来代表搜索的方向,我是使用深度搜索,个人理解认为深度搜索即为递归的升级版
即每个点判断上下左右是否可以,接着进行递归运用,每次到达终点都使计数器加一,以此类推可以得到最后的结果。
代码实现
#include<stdio.h>
int a[6][6],book[6][6],n,m,fx,fy,sum=0;
void dfs(int x,int y)
{
int next[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
int k,tx,ty;
if(x==fx&&y==fy)
{
sum++;
return;
}
for(k=0;k<=3;k++)
{
tx=x+next[k][0];
ty=y+next[k][1];
if(tx<1||ty<1||tx>n||ty>m)
continue;
if(book[tx][ty]==0&&a[tx][ty]==0)
{
book[tx][ty]=1;
dfs(tx,ty);
book[tx][ty]=0;
}
}
return;
}
int main()
{
int sx,sy,t,i,j,p,q;
scanf("%d%d%d",&n,&m,&t);
for(i=1;i<=n;i++)
{
for(j=1;j<=m;j++)
{
a[i][j]=0;
book[i][j]=0;
}
}
scanf("%d%d%d%d",&sx,&sy,&fx,&fy);
for(i=1;i<=t;i++)
{
scanf("%d%d",&p,&q);
a[p][q]=1;
}
book[sx][sy]=1;
dfs(sx,sy);
printf("%d",sum);
getchar();
getchar();
return 0;
}
下面一题基本上是该题改编
马的遍历
题目描述
有一个 n \times mn×m 的棋盘,在某个点 (x, y)(x,y) 上有一个马,要求你计算出马到达棋盘上任意一个点最少要走几步。
输入格式
输入只有一行四个整数,分别为 n, m, x, yn,m,x,y。
输出格式
一个 n \times mn×m 的矩阵,代表马到达某个点最少要走几步(不能到达则输出 -1−1)。
输入输出样例
输入 #1复制
3 3 1 1
输出 #1复制
0 3 2 3 -1 1 2 1 4
说明/提示
数据规模与约定
对于全部的测试点,保证 1 \leq x \leq n \leq 4001≤x≤n≤400,1 \leq y \leq m \leq 4001≤y≤m≤400。
主要思路
与上一题不同的是对于遍历的路线发生改变,不再是简单的上下左右,提高为周围八个方向
类似数学改编题,换汤不换药,下面看代码实现。
#include<stdio.h>
struct note
{
int x;
int y;
int s;
}que[160001];
int main()
{
int head,tail;
int a[401][401];
int book[401][401];
int next[8][2]={{2,-1},{2,1},{1,2},{-1,2},{-2,1},{-2,-1},{-1,-2},{1,-2}};
int i,j,k,n,m,x,y,tx,ty;
scanf("%d%d%d%d",&n,&m,&x,&y);
for(i=1;i<=n;i++)
{
for(j=1;j<=m;j++)
{
a[i][j]=-1;
book[i][j]=0;
}
}
head=1;
tail=1;
que[tail].x=x;
que[tail].y=y;
que[tail].s=0;
tail++;
book[x][y]=1;
a[x][y]=0;
while(head<tail)
{
for(k=0;k<8;k++)
{
tx=que[head].x+next[k][0];
ty=que[head].y+next[k][1];
if(tx>n||ty>m||tx<1||ty<1)
continue;
if(book[tx][ty]==0)
{
book[tx][ty]=1;
que[tail].x=tx;
que[tail].y=ty;
que[tail].s=que[head].s+1;
a[tx][ty]=que[tail].s;
tail++;
}
}
head++;
}
for(i=1;i<=n;i++)
{
for(j=1;j<=m;j++)
{
printf("%d ",a[i][j]);
}
printf("\n");
}
getchar();
getchar();
return 0;
}
发现搜索类型的题目还仅仅搜索不止,还需要回溯,如下面这题,就使用了回溯。
八皇后
题目描述
一个如下的 6 \times 66×6 的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。
上面的布局可以用序列 2\ 4\ 6\ 1\ 3\ 52 4 6 1 3 5 来描述,第 ii 个数字表示在第 ii 行的相应位置有一个棋子,如下:
行号 1\ 2\ 3\ 4\ 5\ 61 2 3 4 5 6
列号 2\ 4\ 6\ 1\ 3\ 52 4 6 1 3 5
这只是棋子放置的一个解。请编一个程序找出所有棋子放置的解。
并把它们以上面的序列方法输出,解按字典顺序排列。
请输出前 33 个解。最后一行是解的总个数。
输入格式
一行一个正整数 nn,表示棋盘是 n \times nn×n 大小的。
输出格式
前三行为前三个解,每个解的两个数字之间用一个空格隔开。第四行只有一个数字,表示解的总数。
输入输出样例
输入 #1复制
6
输出 #1复制
2 4 6 1 3 5 3 6 2 5 1 4 4 1 5 2 6 3 4
说明/提示
【数据范围】
对于 100\%100% 的数据,6 \le n \le 136≤n≤13。
题目翻译来自NOCOW。
USACO Training Section 1.5
回溯即为在搜索结束的时候及时收回数组,最后使数组可以多次使用
代码如下
#include<stdio.h>
int sum=0,n,i,j;
int qwq[14],a[60],b[60],lie[14];
void dfs(int k,int n)
{
for(i=1;i<=n;i++)
{
if(a[k+15-i]==0&&b[i+k]==0&&lie[i]==0)
{
qwq[k]=i;
lie[i]=1;
a[k+15-i]=1;
b[k+i]=1;
if(k==n)
{
printf("1\n");
if(sum<=3)
{
for(i=1;i<=n;i++)
{
printf("%d ",qwq[i]);
}
printf("\n");
}
sum++;
}
else
{
dfs(k+1,n);
}
a[k+15-i]=0;
b[i+k]=0;
lie[i]=0;
}
}
return;
}
int main()
{
scanf("%d",&n);
dfs(1,n);
printf("%d\n",sum);
getchar();
getchar();
return 0;
}