【题解提供者】吴立强
思路
本题本质上是在问方格图四联通的状态下存在多少个极大矩形连通块。
首先需要获得每个极大连通块的数据,这一点可以通过任意一种搜索完成。
其次需要想办法判定当前连通块是否为矩形,可以用一种类似哈希的方式来完成,先求出整个连通块的外轮廓:最小和最大的横纵坐标。如此一来就获得了连通块的最小外接矩形,那么当且仅当连通块大小等于最小外接矩形大小时可以判定该连通块为矩形。
#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)。
扩展
关于矩形的哈希判定还有很多种方式,比如只需要分别判断连通块的每一行和每一列出现的结点个数是否相同也可以得到正确结果,不过不论何种算法,终究需要【搜索】算法参与搜寻连通块的工作。