题目描述
给定一个 m x n 的矩阵,由若干字符 ‘X’ 和 ‘O’构成,’X’表示该处已被占据,’O’表示该处空闲,请找到最大的单入口空闲区域。
解释:
空闲区域是由连通的’O’组成的区域,位于边界的’O’可以构成入口,
单入口空闲区域即有且只有一个位于边界的’O’作为入口的由连通的’O’组成的区域。
如果两个元素在水平或垂直方向相邻,则称它们是“连通”的。
输入描述
第一行输入为两个数字,第一个数字为行数m,第二个数字为列数n,两个数字以空格分隔,1<=m,n<=200。
剩余各行为矩阵各行元素,元素为‘X’或‘O’,各元素间以空格分隔。
输出描述
若有唯一符合要求的最大单入口空闲区域,输出三个数字
- 第一个数字为入口行坐标(0~m-1)
- 第二个数字为入口列坐标(0~n-1)
- 第三个数字为区域大小
三个数字以空格分隔;
若有多个符合要求,则输出区域大小最大的,若多个符合要求的单入口区域的区域大小相同,则此时只需要输出区域大小,不需要输出入口坐标。
若没有,输出NULL。
用例
输入 | 4 4 X X X X X O O X X O O X X O X X |
输出 | 3 1 5 |
说明 | 存在最大单入口区域,入口坐标(3,1),区域大小5 |
输入 | 4 5 X X X X X O O O O X X O O O X X O X X O |
输出 | 3 4 1 |
说明 | 存在最大单入口区域,入口坐标(3,4),区域大小1 |
输入 | 5 4 X X X X X O O O X O O O X O O X X X X X |
输出 | NULL |
说明 | 不存在最大单入口区域 |
输入 | 5 4 X X X X X O O O X X X X X O O O X X X X |
输出 | 3 |
说明 | 存在两个大小为3的最大单入口区域,两个入口坐标分别为(1,3)、(3,3) |
题目解析
这道题我们需要找到最大的单入口空闲区域。因此,我们需要遍历矩阵,找到所有位于边界的 0,然后计算以这些 0 为入口的最大空闲区域的大小。最后,找到最大的空闲区域即可。
对于每个入口,我们可以使用 DFS 算法遍历矩阵,计算以当前入口为起点的空闲区域的大小。在遍历过程中,我们需要标记已经访问过的位置,避免重复计算。同时,如果当前位置不是边界位置,我们需要确保它不是入口,否则,我们会重复计算已经计算过的区域。
C++
#include <iostream>
#include <vector>
#include <string>
#include <unordered_set>
#include <algorithm>
using namespace std;
int rows, cols;
vector<vector<string>> board; // 存储区域
vector<vector<int>> directions = {{0, -1}, {0, 1}, {-1, 0}, {1, 0}}; // 上下左右四个方向
unordered_set<string> visited; // 存储已经访问过的格子的坐标
// dfs函数返回连通块中的格子数量,同时将连通块的边界坐标存入enter中
int dfs(int row, int col, int count, vector<vector<int>>& enter) {
string pos = to_string(row) + "-" + to_string(col); // 将坐标转化为字符串,方便存储到visited中
// 如果当前格子不在区域内,或者是障碍物,或者已经访问过,直接返回当前连通块的格子数量
if (row < 0 || row >= rows || col < 0 || col >= cols || board[row][col] == "X" || visited.count(pos) == 1) {
return count;
}
visited.insert(pos); // 将当前格子标记为已访问
// 如果当前格子在区域的边界上,将其坐标存入enter中
if (row == 0 || row == rows - 1 || col == 0 || col == cols - 1) {
enter.push_back({row, col});
}
count++; // 当前连通块的格子数量加1
// 对当前格子的四个方向进行dfs搜索
for (vector<int>& dir : directions) {
int newRow = row + dir[0];
int newCol = col + dir[1];
count = dfs(newRow, newCol, count, enter);
}
return count;
}
int main() {
cin >> rows >> cols;
board.resize(rows, vector<string>(cols));
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
cin >> board[i][j];
}
}
vector<vector<int>> ans; // 存储所有符合条件的连通块
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
if (board[i][j] == "O" && visited.count(to_string(i) + "-" + to_string(j)) == 0) { // 如果当前格子是未访问过的O格子,进行dfs搜索
vector<vector<int>> enter;
int count = dfs(i, j, 0, enter); // 计算当前连通块的格子数量和边界坐标
if (enter.size() == 1) { // 如果当前连通块只有一个边界,将其存入ans中
vector<int> pos = enter[0];
vector<int> an = {pos[0], pos[1], count};
ans.push_back(an);
}
}
}
}
if (ans.size() == 0) { // 如果没有符合条件的连通块,输出NULL
cout << "NULL" << endl;
} else {
sort(ans.begin(), ans.end(), [](vector<int>& a, vector<int>& b){ // 对符合条件的连通块按照格子数量从大到小排序
return b[2] < a[2];
});
if (ans.size() == 1 || ans[0][2] > ans[1][2]) { // 如果最大的连通块是唯一的,输出其边界坐标和格子数量;否则,只输出最大的连通块的格子数量
string res;
for (int ele : ans[0]) {
res += to_string(ele) + " ";
}
cout << res << endl;
} else {
cout << ans[0][2] << endl;
}
}
return 0;
}
JavaScript
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
let rows, cols;
let board = [];
let directions = [[0, -1], [0, 1], [-1, 0], [1, 0]];
let visited = new Set();
function dfs(row, col, count, enter) {
let pos = row.toString() + "-" + col.toString();
if (row < 0 || row >= rows || col < 0 || col >= cols || board[row][col] === "X" || visited.has(pos)) {
return count;
}
visited.add(pos);
if (row === 0 || row === rows - 1 || col === 0 || col === cols - 1) {
enter.push([row, col]);
}
count++;
for (let dir of directions) {
let newRow = row + dir[0];
let newCol = col + dir[1];
count = dfs(newRow, newCol, count, enter);
}
return count;
}
rl.on('line', function (line) {
if (!rows) {
[rows, cols] = line.split(' ').map(Number);
} else {
board.push(line.split(' '));
if (board.length === rows) {
let ans = [];
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
if (board[i][j] === "O" && !visited.has(i.toString() + "-" + j.toString())) {
let enter = [];
let count = dfs(i, j, 0, enter);
if (enter.length === 1) {
let pos = enter[0];
ans.push([pos[0], pos[1], count]);
}
}
}
}
if (ans.length === 0) {
console.log("NULL");
} else {
ans.sort((a, b) => b[2] - a[2]);
if (ans.length === 1 || ans[0][2] > ans[1][2]) {
console.log(ans[0].join(' '));
} else {
console.log(ans[0][2]);
}
}
rows = cols = null;
board = [];
visited.clear();
}
}
});
Java
import java.util.*;
public class Main {
static int rows, cols;
static List<List<String>> board; // 存储区域
static List<List<Integer>> directions = Arrays.asList(
Arrays.asList(0, -1),
Arrays.asList(0, 1),
Arrays.asList(-1, 0),
Arrays.asList(1, 0)); // 上下左右四个方向
static Set<String> visited = new HashSet<>(); // 存储已经访问过的格子的坐标
// dfs函数返回连通块中的格子数量,同时将连通块的边界坐标存入enter中
static int dfs(int row, int col, int count, List<List<Integer>> enter) {
String pos = row + "-" + col; // 将坐标转化为字符串,方便存储到visited中
// 如果当前格子不在区域内,或者是障碍物,或者已经访问过,直接返回当前连通块的格子数量
if (row < 0 || row >= rows || col < 0 || col >= cols || board.get(row).get(col).equals("X") || visited.contains(pos)) {
return count;
}
visited.add(pos); // 将当前格子标记为已访问
// 如果当前格子在区域的边界上,将其坐标存入enter中
if (row == 0 || row == rows - 1 || col == 0 || col == cols - 1) {
enter.add(Arrays.asList(row, col));
}
count++; // 当前连通块的格子数量加1
// 对当前格子的四个方向进行dfs搜索
for (List<Integer> dir : directions) {
int newRow = row + dir.get(0);
int newCol = col + dir.get(1);
count = dfs(newRow, newCol, count, enter);
}
return count;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
rows = sc.nextInt();
cols = sc.nextInt();
board = new ArrayList<>();
for (int i = 0; i < rows; i++) {
List<String> row = new ArrayList<>();
for (int j = 0; j < cols; j++) {
row.add(sc.next());
}
board.add(row);
}
List<List<Integer>> ans = new ArrayList<>(); // 存储所有符合条件的连通块
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
if (board.get(i).get(j).equals("O") && !visited.contains(i + "-" + j)) { // 如果当前格子是未访问过的O格子,进行dfs搜索
List<List<Integer>> enter = new ArrayList<>();
int count = dfs(i, j, 0, enter); // 计算当前连通块的格子数量和边界坐标
if (enter.size() == 1) { // 如果当前连通块只有一个边界,将其存入ans中
List<Integer> pos = enter.get(0);
List<Integer> an = Arrays.asList(pos.get(0), pos.get(1), count);
ans.add(an);
}
}
}
}
if (ans.size() == 0) { // 如果没有符合条件的连通块,输出NULL
System.out.println("NULL");
} else {
ans.sort((a, b) -> b.get(2) - a.get(2)); // 对符合条件的连通块按照格子数量从大到小排序
if (ans.size() == 1 || ans.get(0).get(2) > ans.get(1).get(2)) { // 如果最大的连通块是唯一的,输出其边界坐标和格子数量;否则,只输出最大的连通块的格子数量
String res = "";
for (int ele : ans.get(0)) {
res += ele + " ";
}
System.out.println(res);
} else {
System.out.println(ans.get(0).get(2));
}
}
}
}
Python
rows, cols = map(int, input().split())
board = []
for i in range(rows):
row = input().split()
board.append(row)
directions = [[0, -1], [0, 1], [-1, 0], [1, 0]]
visited = set()
# dfs函数返回连通块中的格子数量,同时将连通块的边界坐标存入enter中
def dfs(row, col, count, enter):
pos = str(row) + "-" + str(col) # 将坐标转化为字符串,方便存储到visited中
# 如果当前格子不在区域内,或者是障碍物,或者已经访问过,直接返回当前连通块的格子数量
if row < 0 or row >= rows or col < 0 or col >= cols or board[row][col] == "X" or pos in visited:
return count
visited.add(pos) # 将当前格子标记为已访问
# 如果当前格子在区域的边界上,将其坐标存入enter中
if row == 0 or row == rows - 1 or col == 0 or col == cols - 1:
enter.append([row, col])
count += 1 # 当前连通块的格子数量加1
# 对当前格子的四个方向进行dfs搜索
for dir in directions:
newRow = row + dir[0]
newCol = col + dir[1]
count = dfs(newRow, newCol, count, enter)
return count
ans = [] # 存储所有符合条件的连通块
for i in range(rows):
for j in range(cols):
if board[i][j] == "O" and str(i) + "-" + str(j) not in visited: # 如果当前格子是未访问过的O格子,进行dfs搜索
enter = []
count = dfs(i, j, 0, enter) # 计算当前连通块的格子数量和边界坐标
if len(enter) == 1: # 如果当前连通块只有一个边界,将其存入ans中
pos = enter[0]
an = [pos[0], pos[1], count]
ans.append(an)
if len(ans) == 0: # 如果没有符合条件的连通块,输出NULL
print("NULL")
else:
ans.sort(key=lambda x: x[2], reverse=True) # 对符合条件的连通块按照格子数量从大到小排序
if len(ans) == 1 or ans[0][2] > ans[1][2]: # 如果最大的连通块是唯一的,输出其边界坐标和格子数量;否则,只输出最大的连通块的格子数量
res = " ".join(map(str, ans[0]))
print(res)
else:
print(ans[0][2])