题目描述:
听说最近两斑点的奶牛最受欢迎,约翰立即购进了一批两斑点牛。
不幸的是,时尚潮流往往变化很快,当前最受欢迎的牛变成了一斑点牛。
约翰希望通过给每头奶牛涂色,使得它们身上的两个斑点能够合为一个斑点,让它们能够更加时尚。
牛皮可用一个 N×M 的字符矩阵来表示,如下所示:
................
..XXXX....XXX...
...XXXX....XX...
.XXXX......XXX..
........XXXXX...
.........XXX....
其中,X表示斑点部分。
如果两个 X在垂直或水平方向上相邻(对角相邻不算在内),则它们属于同一个斑点,由此看出上图中恰好有两个斑点。
约翰牛群里所有的牛都有两个斑点。
约翰希望通过使用油漆给奶牛尽可能少的区域内涂色,将两个斑点合为一个。
在上面的例子中,他只需要给三个 .. 区域内涂色即可(新涂色区域用 ∗∗ 表示):
................
..XXXX....XXX...
...XXXX*...XX...
.XXXX..**..XXX..
........XXXXX...
.........XXX....
请帮助约翰确定,为了使两个斑点合为一个,他需要涂色区域的最少数量。
输入格式
第一行包含两个整数 N 和 M。
接下来 N 行,每行包含一个长度为 M 的由 X和 .构成的字符串,用来表示描述牛皮图案的字符矩阵。
输出格式
输出需要涂色区域的最少数量。
数据范围
1≤N,M≤50
输入样例:
6 16
................
..XXXX....XXX...
...XXXX....XX...
.XXXX......XXX..
........XXXXX...
.........XXX....
输出样例:
3
解题思路:
根据题目描述,牛上只有两个斑点,可以用BFS宽搜出第一个连通块(即第一个斑点)将其用1标记,再进行宽搜将第二个连通块(即第二个斑点)用2标记。
标记完成后暴力枚举每一个被1标记的点和被2标记的点,枚举所有(1与2)之间的曼哈顿距离(|x1 - x2| + |y1 - y2|)在枚举结果中求出最小值即可。
题解代码(AcWing同步更新):
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
const int N = 60;
char matrix[N][N];
int state[N][N];
int dx[4] = {1, -1, 0, 0};//上下左右扩展偏移量
int dy[4] = {0, 0, 1, -1};
int n, m, flag = 1;
void bfs(int a, int b){
queue<pair<int, int>> q;
q.push({a, b});
state[a][b] = flag;
while(q.size()){
pair<int, int> tmp = q.front();
q.pop();
for(int i = 0; i < 4; i++){//对上下左右四个方向进行搜索
int x = tmp.first + dx[i];
int y = tmp.second + dy[i];
if(x < 1 || x > n || y < 1 || y > m){//出界不合法不扩展
continue;
}
if(state[x][y] != 0){//遍历过不合法
continue;
}
if(matrix[x][y] != 'X'){//不是斑点不做扩展
continue;
}
q.push({x, y});//将合法的斑点坐标入队列
state[x][y] = flag;//将斑点进行标记(flag初始值为1)
}
}
flag++;//下一次遍历连通块flag将变成2,用2标记第二个斑点
}
int main(){
cin >> n >> m;
for(int i = 1; i <= n; i++){
cin >> matrix[i] + 1;
}
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
if(matrix[i][j] == 'X' && state[i][j] == 0){
bfs(i, j);
}
}
}
int res = 999;
for(int x1 = 1; x1 <= n; x1++){//暴力枚举所有被标记为1的点
for(int y1 = 1; y1 <= m; y1++){
if(state[x1][y1] == 1){
for(int x2 = 1; x2 <= n; x2++){//暴力枚举所有被标记为2的点
for(int y2 = 1; y2 <= m; y2++){
if(state[x2][y2] == 2){
int len = abs(x1 - x2) + abs(y1 - y2);//求两点之间的曼哈顿距离
res = min(res, len);//更新最短路径
}
}
}
}
}
}
cout << res - 1 << endl;
return 0;
}