每日总结 12.26

对于搜索题目的实践

深度搜索与广度搜索的区别以及使用范围

                                                    迷宫

题目描述

给定一个 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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值