(A卷,100分)- 机器人(Java & JS & Python)

题目描述

现有一个机器人,可放置于 M × N 的网格中任意位置,每个网格包含一个非负整数编号,当相邻网格的数字编号差值的绝对值小于等于 1 时,机器人可以在网格间移动。

问题: 求机器人可活动的最大范围对应的网格点数目。
说明:网格左上角坐标为 (0,0) ,右下角坐标为(m−1,n−1),机器人只能在相邻网格间上下左右移动

输入描述

第 1 行输入为 M 和 N , M 表示网格的行数 N 表示网格的列数
之后 M 行表示网格数值,每行 N 个数值(数值大小用 k 表示),
数值间用单个空格分隔,行首行尾无多余空格。
M、 N、 k 均为整数,且 1 ≤ M,N ≤ 150, 0 ≤ k ≤ 50

输出描述

输出 1 行,包含 1 个数字,表示最大活动区域的网格点数目,
行首行尾无多余空格。

用例

输入 4 4
1 2 5 2
2 4 4 5
3 5 7 1
4 6 2 4
输出 6
说明

 图中绿色区域,相邻网格差值绝对值都小于等于 1 ,且为最大区域,对应网格点数目为 6。

题目解析

本题其实就是求最大的连通分量,相邻网格是否连通的规则如下

当相邻网格的数字编号差值的绝对值小于等于 1 时

因此,求解连通分量,我们可以使用并查集。关于并查集的知识,请看:

JavaScript算法源码

/* JavaScript Node ACM模式 控制台输入获取 */
const readline = require("readline");

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});

const lines = [];
let m, n;
rl.on("line", (line) => {
  lines.push(line);

  if (lines.length === 1) {
    [m, n] = lines[0].split(" ").map(Number);
  }

  if (m && lines.length === m + 1) {
    lines.shift();
    const matrix = lines.map((line) => line.split(" ").map(Number));

    console.log(getResult(matrix, m, n));
    lines.length = 0;
  }
});

function getResult(matrix, m, n) {
  const ufs = new UnionFindSet(m * n);

  // 上下左右的偏移量
  const offset = [
    [-1, 0],
    [1, 0],
    [0, -1],
    [0, 1],
  ];

  for (let i = 0; i < m; i++) {
    for (let j = 0; j < n; j++) {
      // 注意下面这层for是常量级的,就四次循环,因此,这里的时间复杂度还是O(n*m),即
      for (let k = 0; k < offset.length; k++) {
        const [offsetX, offsetY] = offset[k];
        const newI = i + offsetX;
        const newJ = j + offsetY;
        if (newI < 0 || newI >= m || newJ < 0 || newJ >= n) continue;
        if (Math.abs(matrix[i][j] - matrix[newI][newJ]) <= 1) {
          ufs.union(i * n + j, newI * n + newJ);
        }
      }
    }
  }

  let total = m * n;

  // ufs.count是连通分量的个数,如果只有一个连通分量,那么代表所有的点都可达
  if (ufs.count === 1) return total;

  // 这个for循环是有必要的,确保每个点都指向祖先
  for (let i = 0; i < total; i++) {
    ufs.find(i);
  }

  // 统计指向同一个祖先下点的个数,即每个连通分量下的点个数
  const countObj = ufs.fa.reduce((p, c) => {
    p[c] ? p[c]++ : (p[c] = 1);
    return p;
  }, {});

  // 取最大个数
  return Math.max.apply(null, Object.values(countObj));
}

class UnionFindSet {
  constructor(n) {
    this.fa = new Array(n).fill(0).map((_, i) => i);
    this.count = n;
  }

  find(x) {
    if (x !== this.fa[x]) {
      return (this.fa[x] = this.find(this.fa[x]));
    }
    return x;
  }

  union(x, y) {
    const x_fa = this.find(x);
    const y_fa = this.find(y);

    if (x_fa !== y_fa) {
      this.fa[y_fa] = x_fa;
      this.count--;
    }
  }
}

Java算法源码

import java.util.HashMap;
import java.util.Scanner;

public class Main {
  public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);

    int m = sc.nextInt();
    int n = sc.nextInt();

    int[][] matrix = new int[m][n];
    for (int i = 0; i < m; i++) {
      for (int j = 0; j < n; j++) {
        matrix[i][j] = sc.nextInt();
      }
    }

    System.out.println(getResult(matrix, m, n));
  }

  public static int getResult(int[][] matrix, int m, int n) {
    UnionFindSet ufs = new UnionFindSet(m * n);

    // 上下左右的偏移量
    int[][] offsets = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};

    for (int i = 0; i < m; i++) {
      for (int j = 0; j < n; j++) {
        // 注意下面这层for是常量级的,就四次循环,因此,这里的时间复杂度还是O(n*m),即
        for (int[] offset : offsets) {
          int newI = i + offset[0];
          int newJ = j + offset[1];

          if (newI < 0 || newI >= m || newJ < 0 || newJ >= n) continue;
          // 当相邻网格的数字编号差值的绝对值小于等于 1 时,机器人可以在网格间移动,即表示网格连通,可以合并
          if (Math.abs(matrix[i][j] - matrix[newI][newJ]) <= 1) {
            ufs.union(i * n + j, newI * n + newJ);
          }
        }
      }
    }

    int total = m * n;

    // ufs.count是连通分量的个数,如果只有一个连通分量,那么代表所有的点都可达
    if (ufs.count == 1) return total;

    // 这个for循环是有必要的,确保每个点都指向祖先
    for (int i = 0; i < total; i++) {
      ufs.find(i);
    }

    // 统计指向同一个祖先下点的个数,即每个连通分量下的点个数
    HashMap<Integer, Integer> count = new HashMap<>();
    for (int i : ufs.fa) {
      count.put(i, count.getOrDefault(i, 0) + 1);
    }

    // 取最大个数,这里必然有值,因此不需要在get前判空
    return count.values().stream().max((a, b) -> a - b).get();
  }
}

// 并查集
class UnionFindSet {
  int[] fa;
  int count;

  public UnionFindSet(int n) {
    this.fa = new int[n];
    this.count = n;
    for (int i = 0; i < n; i++) this.fa[i] = i;
  }

  public int find(int x) {
    if (x != this.fa[x]) {
      return (this.fa[x] = this.find(this.fa[x]));
    }
    return x;
  }

  public void union(int x, int y) {
    int x_fa = this.find(x);
    int y_fa = this.find(y);

    if (x_fa != y_fa) {
      this.fa[y_fa] = x_fa;
      this.count--;
    }
  }
}

Python算法源码

# 并查集
class UnionFindSet:
    def __init__(self, n):
        self.fa = [idx for idx in range(n)]
        self.count = n

    def find(self, x):
        if x != self.fa[x]:
            self.fa[x] = self.find(self.fa[x])
            return self.fa[x]
        return x

    def union(self, x, y):
        x_fa = self.find(x)
        y_fa = self.find(y)

        if x_fa != y_fa:
            self.fa[y_fa] = x_fa
            self.count -= 1


m, n = map(int, input().split())

matrix = []
for i in range(m):
    matrix.append(list(map(int, input().split())))

ufs = UnionFindSet(m * n)

# 上下左右的偏移量
offsets = ((-1, 0), (1, 0), (0, -1), (0, 1))

for i in range(m):
    for j in range(n):
        # 注意下面这层for是常量级的,就四次循环,因此,这里的时间复杂度还是O(n*m),即
        for offsetX, offsetY in offsets:
            newI = i + offsetX
            newJ = j + offsetY

            if 0 <= newI < m and 0 <= newJ < n and abs(matrix[i][j] - matrix[newI][newJ]) <= 1:
                ufs.union(i * n + j, newI * n + newJ)

total = m * n

# ufs.count是连通分量的个数,如果只有一个连通分量,那么代表所有的点都可达
if ufs.count == 1:
    print(total)
else:
    # count字典用于统计指向同一个祖先下点的个数,即每个连通分量下的点个数
    count = {}

    # 这个for循环是有必要的,确保每个点都指向祖先
    for i in range(total):
        ufs.find(i)
        fa = ufs.fa[i]
        count[fa] = count.get(fa, 0) + 1

    # 取最大个数
    print(max(count.values()))

免责声明:

1、IT资源小站为非营利性网站,全站所有资料仅供网友个人学习使用,禁止商用
2、本站所有文档、视频、书籍等资料均由网友分享,本站只负责收集不承担任何技术及版权问题
3、如本帖侵犯到任何版权问题,请立即告知本站,本站将及时予与删除下载链接并致以最深的歉意
4、本帖部分内容转载自其它媒体,但并不代表本站赞同其观点和对其真实性负责
5、一经注册为本站会员,一律视为同意网站规定,本站管理员及版主有权禁止违规用户
6、其他单位或个人使用、转载或引用本文时必须同时征得该帖子作者和IT资源小站的同意
7、IT资源小站管理员和版主有权不事先通知发贴者而删除本文

0

评论0

站点公告

没有账号?注册  忘记密码?