方格图剪纸游戏_题解

【题解提供者】吴立强

思路

本题本质上是在问方格图四联通的状态下存在多少个极大矩形连通块。

首先需要获得每个极大连通块的数据,这一点可以通过任意一种搜索完成。

其次需要想办法判定当前连通块是否为矩形,可以用一种类似哈希的方式来完成,先求出整个连通块的外轮廓:最小和最大的横纵坐标。如此一来就获得了连通块的最小外接矩形,那么当且仅当连通块大小等于最小外接矩形大小时可以判定该连通块为矩形。

#include <iostream>
#include <queue>
using namespace std;
#define _for(i, a, b) for(int i = a, I_MAX = b; i <= I_MAX; i ++)  /// 宏函数优化代码外观
typedef pair<int, int> pii;

const int N = 1009;

char s[N][N];  /// 存储原字符矩形
int n, m;

int xx[] = {0, 1, 0, -1, 0};  /// 四联通的四个方向
bool bfs(int x, int y) {  /// 广度优先搜索
	queue<pii> qu;
	qu.push({x, y});  s[x][y] = '*';
	int mn[2] = {x, y};  /// 存储横、纵坐标最小值
	int mx[2] = {x, y};  /// 存储横、纵坐标最大值
	int sum = 0;  /// 记录连通块内部结点个数
	while(!qu.empty()) {
		x = qu.front().first, y = qu.front().second;
		qu.pop();  sum ++;  /// 统计结点数
		mn[0] = min(mn[0], x);  /// 更新对应最值
		mn[1] = min(mn[1], y);
		mx[0] = max(mx[0], x);
		mx[1] = max(mx[1], y);
		_for(i, 1, 4) {  /// 四个方向扩散
			int fx = x + xx[i], fy = y + xx[i - 1];  /// 获取新的坐标
			if(fx < 1 || fx > n || fy < 1 || fy > m) continue;
			if(s[fx][fy] == '*') continue;
			s[fx][fy] = '*';  /// 标记当前位置已经被访问过
			qu.push({fx, fy});
		}
	}
	return sum == (mx[0] - mn[0] + 1) * (mx[1] - mn[1] + 1);  /// 判断是否为矩形
}
void solved() {
	cin >> n >> m;
	_for(i, 1, n) cin >> s[i] + 1;
	int ans = 0;
	_for(i, 1, n) _for(j, 1, m)  /// 扫描每个位置统计答案即可,bfs保证每个连通块只会被统计一次
		if(s[i][j] == '.') ans += bfs(i, j);
	cout << ans;
}
signed main() {
	solved();
	return 0;
}

算法分析

该算法的时间复杂度为 O ( n × m ) O(n\times m) O(n×m)

扩展

关于矩形的哈希判定还有很多种方式,比如只需要分别判断连通块的每一行和每一列出现的结点个数是否相同也可以得到正确结果,不过不论何种算法,终究需要【搜索】算法参与搜寻连通块的工作。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值