萌新做点小玩意儿DAY-13 DFS解决城堡问题

城堡问题是一个典型的可以用DFS来解决的地图问题,跟之前的面积问题一样,地图类问题都少不了方向向量,因为问题描述比较复杂从问题描述来考虑算法的思路。城堡问题可以算是面积问题的一个基础问题,解决起来比较轻松而且适合接触刚DFS的新手来练手。

问题描述:有一个城堡的地形图,如图所示,其中#表示墙不可跨越的区域,而---和—表示可以跨越的区域,墙体围成的每一个闭合区域称为一个房间,编写一个程序计算一共有多少个房间,最大的房间有多大。

其中城堡被分割为MxN(M和N不大于50)个方块,每个方块可以有0-4面墙,在输入数据时,输入城堡的行和列的方块数,每一个方块用一个数字来描述,数字表示方块周围存在的所有墙的代码和,1表示存在西墙,2表示存在北墙,4表示存在东墙,8表示存在南墙,例如(1,1)所示的方格输入的数值应为1+2+8=11。

算法思想:1、类比面积问题,因为输入的数据的确保了四周都是墙壁,所有我们不需要考虑在遍历的时候是不是会出界,我们不妨把方块看做节点问题,把能互通的方块连接起来,形成若干个连通图,而目的就是求有多少个极大连通子图,也不难找到节点数最多的那个连通子图。对于每一个房间的所有方块我们可以用同一个标号来进行“染色”,想到这里我们需要定义的全局变量就很明确了,首先是行R,列C;二维数组:房间room[][],相对应的染色编号[][];还要有房间数目RoomNum,最大房间面积MaxRoomArea,遍历到的当前的房间面积RoomArea

2、明确了我们要用到的变量以后,DFS就少不了DFS函数还有剪枝限界函数,在这个问题上我们要输出的是房间的总个数和最大面积,不需要对图进行剪枝,限界方面因为输入的数据中四周方块一定包含墙体,我们也不需要考虑限界。就主要从DFS算法来考虑,很好想到的是DFS函数的两个参数应该是行i和列j,这里给大家一个通用的DFS遍历图节点的伪代码,

DFS(节点V){
	if(V已经被遍历过)
		return;
	将V进行标记;
	对V相邻的所有点U{
		DFS(U);
	}
}

判断是否遍历过就用到color数组,只要color[i][j]不为0则就是遍历过的旧节点,对于V相邻的所有点有四个方向,也就说明需要向四个方向都做DFS,这里墙的编号比较巧妙,1、2、4、8的二进制数分别为0001,0010,0100,1000后四位的每一位分别为1,也就是说我们只需要用room[i][j]进行&运算,room[i][j]&1 == 0时,西边是没有墙的,可以向西进行遍历,以此类推其余三个方向。

3、main函数要实现的工作就是输入整个城堡(R,C,room),对color数组初始化,在两次循环下调用DFS

源代码如下,因为这个题目比较经典所以算法思路写的非常详细了,源代码也是没有加注释了。

#include <iostream>
#include <windows.h>
using namespace std;

int R,C;
int room[60][60];
int color[60][60];
int MaxRoomArea = 0, RoomNum = 0;
int RoomArea;

void DFS(int i, int j){
    if(color[i][j]){
        return;
    }
    ++RoomArea;
    color[i][j] = RoomNum;
    if((room[i][j] & 1) == 0) DFS(i, j-1);
    if((room[i][j] & 2) == 0) DFS(i-1,j);
    if((room[i][j] & 4) == 0) DFS(i, j+1);
    if((room[i][j] & 8) == 0) DFS(i+1, j);
}

int main(){
    cin >>R>>C;
    for(int i = 1; i <= R; ++i)
		for(int j = 1; j <= C; ++j)
			cin >>room[i][j];
	memset(color, 0, sizeof(color));
	for(int m = 1; m <= R; ++m)
		for(int n = 1; n <= C; ++n){
			if(!color[m][n]){
				++RoomNum;
				RoomArea = 0;
				DFS(m, n);
				MaxRoomArea = max(RoomArea, MaxRoomArea);
			}
		}
	cout <<RoomNum<<endl;
	cout <<MaxRoomArea<<endl;
	return 0;
}

程序的测试图: 最后整个算法的时间复杂度是O(R*C)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值