【华为OD机试 2023】 查找单入口空闲区域(C++ Java JavaScript Python)

题目描述

给定一个 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])
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值