IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 池塘抽水 c++ dfs详解 -> 正文阅读

[数据结构与算法]池塘抽水 c++ dfs详解

题目描述

题目描述

??你刚刚承包了一块大小为 M * N (1<=M, N<=1000)的地块,现要利用这片区域开发成商业用地,但之前该地块是一个渔场所在地,因此分布着许多大小不一的池塘,现在需要把所有的这些池塘里的水抽干并填平。 现在你想知道至少需要多少台抽水机器才能将这片区域的所有池塘抽干。如果池塘的区域互相连接,那么这些区域可以使用同一台机器。例如:若 M = 1, N = 5,其中(1, 1), (1, 2)和(1, 4)区域有池塘,那么由于(1, 1)和(1, 2)这两块区域连在一起,所以只需要两台机器即可采完所有池塘。(注意,池塘如果上、下、左、右相邻以及对角线相邻都属于相连的情况)

输入格式

??输入的数据有多组,每组数据首先是两个正整数 M 和 N,表示地块的尺寸,接下来是一个 M 行N 列的矩阵,矩阵中有两种可能的值,一种是“*”,表示此处没有池塘,一种是“@”,代表此处为池塘。若 M, N 均为 0,则表示输入结束,该组数据无需输出任何结果。

输出格式

???对于每组数据,在一行内输出需要的抽水机器数量。

输入样例

1 1
*
3 5
*@*@*
**@**
*@*@*
1 8
@@****@*
5 5
****@
*@@*@
*@**@
@@@*@
@@**@
0 0

输出样例

0
1
2
2

解题思路

题意

??简单的说,题目是求独立的池塘的个数。把地块按下标分成 M * N 的方格,每个池塘方格与上、下、左、右相邻以及对角线相邻的池塘方格相连。输入中 * 表示没有池塘的方格, @ 表示有池塘的方格。当输入(0,0)时,结束输出。

思路

??遍历所有的地块方格,每找到一个池塘方格,放置抽水机将这一整片池塘抽干(把 @ 变成 * 或者其他字符),重复操作直到遍历结束,得到总共抽干池塘的次数也就是需要的抽水机数量。

??使用深度优先搜索思想,先定位到一个池塘方格,把当前方格抽干并依次往相邻的八个方向移动,继续抽干当前的池塘方格和向周围搜索的操作,直到移动到地图边界或者非池塘的方格,不做任何操作并返回上一个方格,继续向其它方向搜索。最终回到初始位置完成递归搜索。因为递归过程是将当前的池塘方格抽干并移动至周围的方格,当前方格的状态不影响移动操作,所以不需要额外的回溯操作。

样例图解

5    5
*  *  *  *  @
*  @  @  *  @
*  @  *  *  @
@  @  @  *  @
@  @  *  *  @

用蓝色方格代表池塘方格,绿色方格代表非池塘方格。

地块原状态
请添加图片描述

从(1,1)位置从左往右、从上往下遍历,找到第一个位置(1,5),放入第一台抽水机
在这里插入图片描述

进行递归抽水的操作,首先将当前位置池塘方格抽干
在这里插入图片描述
然后判断,左下、左、左上、上、右上、右、右下均不合要求,移动到下面一格
请添加图片描述
重复抽水和移动操作至填满当前池塘
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

请添加图片描述
无可抽水位置,按移动位置依次回退,每回退一个位置继续判断是否可以抽水
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

回到初始位置(1,5),判断结束后退出抽水操作并开始搜索下一个池塘方格位置,放置第二台抽水机(2,2)
在这里插入图片描述
继续抽水并以左下,左,左上,上,右上,右,右下,下的顺序移动搜索
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
依次回退上一位置并继续判断,直到回到第二台抽水机位置(2,2),完成抽水操作。
在这里插入图片描述
继续搜索放置饮水机的位置,遍历完地块后算法结束
在这里插入图片描述
总共抽水机台数为2台

解题代码

定义变量

定义地块方格数组 block 和地块尺寸变量 M、N,记录抽水机数量的变量 cnt

const int SIZE = 1007;
int cnt = 0;
int M, N;
char block[SIZE][SIZE];

定义实现左下、左、左上、上、右上、右、右下、下移动的数组

int x[8] = { 1, 0, -1, -1, -1, 0, 1, 1 };
int y[8] = { -1, -1, -1, 0, 1, 1, 1, 0 };

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TRhb2HqB-1637423849988)(D:.myWords\解题markdown\池塘抽水.assets\image-20211120230015961.png)]
通过原坐标(px, py)加上(x[i], y[i])实现坐标的移动(px + x[i], py + y[i]),例

int px = 2, py = 2;
for (int i = 0; i < 8; i++)
{
    //依次添加坐标,向指定方向移动
	px += x[i];
	py += y[i];
    cout << "(" << px << "," << py << ")" << endl;
    //回到移动前位置,减去添加的坐标
    px -= x[i];
    py -= y[i];
}

/*
输出结果
(3,1)
(2,1)
(1,1)
(1,2)
(1,3)
(2,3)
(3,3)
(3,2)
*/

读入数据

读入多组数据,读到(0,0)结束

while (cin >> M >> N)
{
    if (M == 0 && N == 0)
    {
        break;
    }
}

读入地块

void readBlock()
{
	for (int i = 1; i <= M; i++)
	{
		for (int j = 1; j <= N; j++)
		{
//scanf("%c", &block[i][j]) 和 block[i][j] = getchar() 需要处理输入时换行时的 '\n'
//例如在换行后的读入前加 getchar()
			cin >> block[i][j];
		}
	}
}

算法实现

初始化 cnt 变量,遍历地块,寻找到池塘方格时放置抽水机并开始抽水

void solve()
{
	cnt = 0;
	for (int i = 1; i <= M; i++)
	{
		for (int j = 1; j <= N; j++)
		{
			if (block[i][j] == '@')
			{
				//放置抽水机,数量+1
				cnt++;
                 //以当前位置开始进入函数进行深搜抽水
				dfs(i, j);
			}
		}
	}
}

深搜实现

void dfs(int px, int py)
{
    //判断是否超过地块范围,超过则停止操作回到上一位置
	if (px < 1 || px > M || py < 1 || py > N)
		return;
	//判断当前位置,不是池塘方格则停止操作,回到上一位置
	if (block[px][py] != '@')
		return;
	
    //抽掉当前位置的水
	block[px][py] = '*';

    //遍历八个方向,依次移动
	for (int i = 0; i < 8; i++)
	{
        //添加相应坐标移动位置
		px += x[i], py += y[i];
        //以移动后的位置进入深搜程序,执行抽水和移动操作
		dfs(px, py);
     	//当前位置(px, py)深搜完后,减去添加的坐标返回移动之前的位置,继续其它位置进行深搜
		px -= x[i], py -= y[i];
	}
}

输出结果

直接输出抽水机数量 cnt

cout << cnt << endl;

整体代码

#include <iostream>

using namespace std;

const int SIZE = 1007;
int cnt = 0;
int M, N;
char block[SIZE][SIZE];

int x[8] = { 1, 0, -1, -1, -1, 0, 1, 1 };
int y[8] = { -1, -1, -1, 0, 1, 1, 1, 0 };

void readBlock()
{
	for (int i = 1; i <= M; i++)
	{
		for (int j = 1; j <= N; j++)
		{
			cin >> block[i][j];
		}
	}
}

void dfs(int px, int py)
{
	if (px < 1 || px > M || py < 1 || py > N)
		return;

	if (block[px][py] != '@')
		return;

	block[px][py] = '*';

	for (int i = 0; i < 8; i++)
	{
		px += x[i], py += y[i];
		dfs(px, py);
		px -= x[i], py -= y[i];
	}
}

void solve()
{
	cnt = 0;
	for (int i = 1; i <= M; i++)
	{
		for (int j = 1; j <= N; j++)
		{
			if (block[i][j] == '@')
			{
				cnt++;
				dfs(i, j);
			}
		}
	}
	cout << cnt << endl;
}

int main()
{
	while (cin >> M >> N)
	{
		if (M == 0 && N == 0)
		{
			break;
		}
		readBlock();
		solve();
	}

	return 0;
}

运行结果

在这里插入图片描述

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-11-22 12:35:49  更:2021-11-22 12:35:59 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 12:53:02-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码