DFS学习2

1.迷宫

题目描述

给定一个 ​ 方格的迷宫,迷宫里有 ​ 处障碍,障碍处不可通过。

在迷宫中移动有上下左右四种方式,每次只能移动一个方格。数据保证起点上没有障碍。

给定起点坐标和终点坐标,每个方格最多经过一次,问有多少种从起点坐标到终点坐标的方案。

输入格式

第一行为三个正整数 ​,分别表示迷宫的长宽和障碍总数。

第二行为四个正整数 ​,​ 代表起点坐标,​ 代表终点坐标。

接下来 ​ 行,每行两个正整数,表示障碍点的坐标。

输出格式

输出从起点坐标到终点坐标的方案总数。

样例 #1

样例输入 #1

2 2 1
1 1 2 2
1 2

样例输出 #1

1

思路与代码

经典迷宫问题,

每个方格最多经过一次,问有多少种从起点坐标到终点坐标的方案。

所以需要 存某个位置是否走过,保证不能走重复路,而且需要擦掉脚印

对于感叹号注释的那一行,是对起点的标记,使起点不能被重复访问。

由于只有一个起点,所以在main函数里把起点标成已被访问,也是可以的。

int dx[] = {0, 0, 1, -1};   int dy[] = {1, -1, 0, 0};
void dfs(int x, int y)
{
    if(x == fx && y == fy)
    {
        cnt++;
        return;
    }
    v[x][y] = 1;        //  !!!!!!!!!
    for(int k = 0; k < 4; k++)
    {
        int xi = dx[k] + x, yi = dy[k] + y; 
        if(xi >= 1 && xi <= n && yi >= 1 && yi <= m)    //不越界 
        {
            if( (a[xi][yi] == 0) && v[xi][yi] == 0)     //脚印
            {
                v[xi][yi] = 1;
                dfs(xi, yi);
                v[xi][yi] = 0;
            }
        }
    }
}

2.入门

题目描述

不是任何人都可以进入桃花岛的,黄药师最讨厌像郭靖一样呆头呆脑的人。所以,他在桃花岛的唯一入口处修了一条小路,这条小路全部用正方形瓷砖铺设而成。有的瓷砖可以踩,我们认为是安全的,而有的瓷砖一踩上去就会有喷出要命的毒气,那你就死翘翘了,我们认为是不安全的。你只能从一块安全的瓷砖上走到与他相邻的四块瓷砖中的任何一个上,但它也必须是安全的才行。

由于你是黄蓉的朋友,她事先告诉你哪些砖是安全的、哪些砖是不安全的,并且她会指引你飞到第一块砖上(第一块砖可能在任意安全位置),现在她告诉你进入桃花岛的秘密就是:如果你能走过最多的瓷砖并且没有死,那么桃花岛的大门就会自动打开了,你就可以从当前位置直接飞进大门了。

注意:瓷砖可以重复走过,但不能重复计数。

输入格式

第一行两个正整数 ​ 和 ​,分别表示小路的宽度和长度。

以下 ​ 行为一个 ​ 的字符矩阵。每一个字符代表一块瓷砖。其中,. 代表安全的砖,# 代表不安全的砖,@ 代表第一块砖。

输出格式

输出一行,只包括一个数,即你从第一块砖开始所能安全走过的最多的砖块个数(包括第一块砖)。

样例 #1

样例输入 #1

11 9
.#.........
.#.#######.
.#.#.....#.
.#.#.###.#.
.#.#..@#.#.
.#.#####.#.
.#.......#.
.#########.
...........

样例输出 #1

59

思路与代码

迷宫问题,

从起点开始,依次向上下左右四个方向搜索,如果是点就可以走,是#就不能走,

由于这道题是求一共有几个点可以走到,所以单独用一个 cnt 数组存这个点是否能够走到,仍然用v数组存是否访问过

但是有一个数据点超时了呢

int dx[] = {0, 0, 1, -1};
int dy[] = {1, -1, 0, 0};
​
void dfs(int x, int y)
{
    for(int k = 0; k < 4; k++)
    {
        int xi = dx[k] + x, yi = dy[k] + y;
        
        if(xi >= 1 && xi <= h && yi >= 1 && yi <= w)    //不越界
        {
            if( (c[xi][yi] == '.') && v[xi][yi] == 0)   //
            {
                v[xi][yi] = 1, cnt[xi][yi] = 1;
                dfs(xi, yi);
                v[xi][yi] = 0;
            }
        }
    }
    
}

对比了一下全AC的代码,发现没有必要再走已经走过的路,就是说搜索的时候,不需要擦掉脚步从头搜索了。

可见,dfs的模板可以套上所有搜索的题,但是细节部分还是要根据具体题目处理,不然会TLE

相比于第一题而言,这一题的搜索不需要从起点出发,只要保证每个地方走过一次就行,且不用担心有能走的地方没走到。

修改后如下

void dfs(int x, int y)
{
    for(int k = 0; k < 4; k++)
    {
        int xi = dx[k] + x, yi = dy[k] + y; 
        if(xi >= 1 && xi <= h && yi >= 1 && yi <= w)    //不越界 
        {
            if( (c[xi][yi] == '.') && v[xi][yi] == 0)
            {
                v[xi][yi] = 1;                          //!!!!!!
                dfs(xi, yi);                            //!!!!!!
            }
        }
    }   
}

3.马的遍历

题目描述

有一个 ​ 的棋盘,在某个点 ​ 上有一个马,要求你计算出马到达棋盘上任意一个点最少要走几步。

输入格式

输入只有一行四个整数,分别为 ​。

输出格式

一个 ​ 的矩阵,代表马到达某个点最少要走几步(不能到达则输出 ​)。

样例 #1

样例输入 #1

3 3 1 1

样例输出 #1

0    3    2    
3    -1   1    
2    1    4

提示

数据规模与约定

对于全部的测试点,保证 ​,​。

思路与代码

x,y是起点,fx,fy是终点,time存步数,思路完全符合dfs的模板,

不过,精致的代码换来的却是满屏的TLE,只好看看能不能剪枝

感叹号注释的第一行是剪枝,结果只少了一个TLE

感叹号注释的第二行,对应第一题放在main函数里的做法。

#include<bits/stdc++.h>
using namespace std;

int n, m, x, y;
int dx[] = {2, 1,  2,  1, -1, -1, -2, -2};
int dy[] = {1, 2, -1, -2,  2, -2, -1,  1};	
int a[401][401], v[401][401];

void dfs(int x, int y, int fx, int fy, int time)
{
    if(a[fx][fy] != -1 && time >= a[fx][fy])return;		///	!!!!!!!!!
	if(x == fx && y == fy)
	{
		if(a[fx][fy] == -1) a[fx][fy] = time;
		else	a[fx][fy] = min(a[fx][fy], time);
		return;
	}									
	for(int k = 0; k < 8; k++)
	{
		int tx = x + dx[k], ty = y + dy[k];
		if(tx <= n && tx >= 1 && ty <= m && ty >= 1)	//不越界
		{
			if(!v[tx][ty])
			{
				v[tx][ty] = 1;
				dfs(tx, ty, fx, fy, time + 1);
				v[tx][ty] = 0;
			}
		}
	}
    
}
int main()
{
	cin >> n >> m >> x >> y;
	memset(a, -1, sizeof(a));
    
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= m; j++)
			memset(v, 0, sizeof(v)), v[x][y] = 1, dfs(x, y, i, j, 0);	!!!!!!!!!!

	for(int i = 1; i <= n; i++)
	{
		for(int j = 1; j <= m; j++)
			cout << a[i][j] << " ";
		cout << endl;
	}
}

一时之间也想不出很好的剪枝方法了。题解里的办法都是bfs,只能等以后再做做看。

不过前面的迷宫是搜1个的最优解,而这道题是分别搜m * n个的最优解,想想也是,

前面的题搜一个的时间大于等于1ms甚至几百ms,而这道题,m*n<=160000,不超时才怪呢。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值