【备战秋招】每日一题:2023.05-B卷-华为OD机试 - 相同数字组成图形的周长

为了更好的阅读体检,可以查看我的算法学习网
本题在线评测链接:P1355

题目描述

有一个 64 64 64x 64 64 64的矩阵,每个元素的默认值为 0 0 0,现在向里面填充数字,相同的数字组成一个实心图形,如下图所示是矩阵的局部(空白表示填充 0 0 0)

0 0 0 1 1 1 2 2 2 3 3 3 4 4 4 5 5 5 6 6 6 7 7 7 8 8 8 9 9 9 10 10 10 11 11 11
0 0 0
1 1 11
2 2 2111
3 3 311122
4 4 411112222
5 5 51122222
6 6 622222
7 7 722222
8 8 8
9 9 9
10 10 10
11 11 11

输入描述

  1. 第一行输入 N N N,表示 N N N个图形, N > 0 N > 0 N>0 N < 64 N < 64 N<64x 64 64 64
  2. 矩阵左上角单元格坐标记作( 0 , 0 0,0 0,0),第一个数字表示行号,第二个数字表示列号
  3. 接下来是 N N N行,每行第一个数是矩阵单元格填充的数字,后续每两个一组,表示填充该数字的单元格坐标
  4. 答题者无需考虑数据格式非法的场景,题目用例不考察数据格式
  5. 题目用例保证同一个填充值只会有一行输入数据

输出描述

  • 一共输出 N N N个数值,每个数值表示某一行输入表示图形的周长
  • 输出顺序需和输入的隔行顺序保持一致,即第 1 1 1个数是输入的第 1 1 1个图形的周长,第 2 2 2个数是输入的第 2 2 2个图形的周长,以此类推。

输入

2
1 1 3 2 2 2 3 2 4 3 2 3 3 3 4 4 1 4 2 4 3 4 4 5 2 5 3
2 3 7 3 8 4 5 4 6 4 7 4 8 5 4 5 5 5 6 5 7 5 8 6 4 6 5 6 6 6 7 6 8 7 4 7 5 7 6 7 7 7 8

输出

18 20

说明

思路:思维 + 遍历

对于矩阵中的每个同色连通块,其周长的计算为最外面的格子对应的最外面的边的长度之和。

于是问题转换为求出所有的同色连通块:

  • 对于每个同色连通块,找到其所有处于连通块边缘的每个格子
  • 对于这些格子,其中最终对周长有所贡献的边,必然不会与这个同色连通块中其他格子共用一条边

继续考虑,对于每个有颜色的格子,有 4 4 4条边,如果其四个方向上的格子的颜色都与其相同,则说明这个格子是一个内部的格子。
否则,这个格子必然是某一个同色连通块的边缘格子,考虑这个格子中对所处同色连通块有贡献的边的数量即可。

故至此,问题转换为求每个有颜色的格子,其上下左右四个相邻格子与其颜色不相等的颜色数量。

时间复杂度: O ( n 2 ) , n = 64 O(n^2), n = 64 O(n2),n=64

类似题目推荐

代码

C++

#include <bits/stdc++.h>
using namespace std;

int n = 64;

const int dx[] = {-1, 0, 1, 0};
const int dy[] = {0, 1, 0, -1};

// 将一行数据拆分成若干个数,装进一个vector中
vector<int> split_to_vector(const string& s) {
    vector<int> vec;
    for (int i = 0; i < s.size(); ++i) {
        if (!isdigit(s[i])) continue;
        int t = 0;
        int j = i;
        while (j < s.size() && isdigit(s[j])) {
            t = t * 10 + (s[j] - '0');
            j += 1;
        }
        vec.emplace_back(t);
        i = j - 1;
    }
    return vec;
}

int main()
{
    int m;
    cin >> m;
    // 忽略输入 m 后留在缓冲区的换行符
    cin.ignore();

    // g 表示图形
    vector<vector<int>> g(n, vector<int>(n));
    // nums 表示所有出现在 g 中的值
    vector<int> nums;

    string s;
    // C++ 读取一行方式
    while(getline(cin, s)) {
        if (s.empty()) continue;
        vector<int> vec = split_to_vector(s);

        nums.push_back(vec[0]);
        for (int j = 1; j + 1 < vec.size(); j += 2) {
            // 给对应的格子赋值
            g[vec[j]][vec[j + 1]] = vec[0];
        }
    }

    // 统计每个值的周长之和
    map<int, int> val;
    for (int i = 0; i < n; ++i)
        for (int j = 0; j < n; ++j)
            // 当一个格子有值,才可能有边长
            if (g[i][j] > 0) {
                int v = g[i][j];
                int res = 4;
                for (int k = 0; k < 4; ++k) {
                    int nx = i + dx[k], ny = j + dy[k];
                    if (nx >= 0 && nx < n && ny >= 0 && ny < n && g[nx][ny] == v) {
                        res -= 1;
                    }
                }
                val[v] += res;
            }

    for (int i = 0; i < m; ++i) {
        cout << val[nums[i]] << " \n"[i + 1 == m];
    }

    return 0;
}

python

n = 64
dx = [-1, 0, 1, 0]
dy = [0, 1, 0, -1]

# g 表示图形
g = [[0] * n for i in range(n)]

# nums 表示所有出现在 g 中的值
nums = []

m = int(input())
for i in range(m):
    lst = list(map(int, input().split()))
    nums.append(lst[0])
    for j in range(1, len(lst), 2):
        # 给对应的格子赋值
        g[lst[j]][lst[j + 1]] = lst[0]

# 统计每个值的周长之和
val = {}
for i in range(n):
    for j in range(n):
        # 当一个格子有值,才可能有边长
        if g[i][j] > 0:
            v = g[i][j]
            res = 4
            for k in range(4):
                nx, ny = i + dx[k], j + dy[k]
                if 0 <= nx < n and 0 <= ny < n and g[nx][ny] == v:
                    res -= 1
            val[v] = val.get(v, 0) + res

for i in range(m):
    print(val[nums[i]], end="\n" if i + 1 == m else " ")

Java

import java.util.*;

public class Main {
    static int n = 64;
    static int[] dx = {-1, 0, 1, 0};
    static int[] dy = {0, 1, 0, -1};

    // g 表示图形
    static int[][] g = new int[n][n];

    // nums 表示所有出现在 g 中的值
    static List<Integer> nums = new ArrayList<>();

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int m = Integer.parseInt(sc.nextLine());

        for (int i = 0; i < m; i++) {
            String[] line = sc.nextLine().split(" ");
            int v = Integer.parseInt(line[0]);
            nums.add(v);
            for (int j = 1; j < line.length; j += 2) {
                int x = Integer.parseInt(line[j]);
                int y = Integer.parseInt(line[j + 1]);
                // 给对应的格子赋值
                g[x][y] = v;
            }
        }

        // 统计每个值的周长之和
        Map<Integer, Integer> val = new HashMap<>();
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                // 当一个格子有值,才会有边长
                if (g[i][j] > 0) {
                    int v = g[i][j];
                    int res = 4;
                    for (int k = 0; k < 4; ++k) {
                        int nx = i + dx[k], ny = j + dy[k];
                        if (nx >= 0 && nx < n && ny >= 0 && ny < n && g[nx][ny] == v) {
                            res -= 1;
                        }
                    }
                    val.put(v, val.getOrDefault(v, 0) + res);
                }
            }
        }

        for (int i = 0; i < m; i++) {
            System.out.print(val.get(nums.get(i)));
            if (i + 1 == m) {
                System.out.println();
            } else {
                System.out.print(" ");
            }
        }
    }
}

Go

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

const n = 64

var dx = [4]int{-1, 0, 1, 0}
var dy = [4]int{0, 1, 0, -1}

// g 表示图形
var g [n][n]int

// nums 表示所有出现在 g 中的值
var nums []int

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	scanner.Scan()
	m, _ := strconv.Atoi(scanner.Text())

	nums = make([]int, m)
	for i := 0; i < m; i++ {
		scanner.Scan()
		line := scanner.Text()

		s := strings.Split(line, " ")
		v, _ := strconv.Atoi(s[0])
		nums[i] = v

		for j := 1; j < len(s); j += 2 {
			x, _ := strconv.Atoi(s[j])
			y, _ := strconv.Atoi(s[j+1])
			// 给对应的格子赋值
			g[x][y] = v
		}
	}

	// 统计每个值的周长之和
	val := make(map[int]int)
	for i := 0; i < n; i++ {
		for j := 0; j < n; j++ {
			// 当一个格子有值,才可能有边长
			if g[i][j] > 0 {
				v := g[i][j]
				res := 4
				for k := 0; k < 4; k++ {
					nx := i + dx[k]
					ny := j + dy[k]
					if nx >= 0 && nx < n && ny >= 0 && ny < n && g[nx][ny] == v {
						res -= 1
					}
				}
				val[v] += res
			}
		}
	}

	for i := 0; i < m; i++ {
		fmt.Print(val[nums[i]])
		if i+1 == m {
			fmt.Println()
		} else {
			fmt.Print(" ")
		}
	}
}

Js

process.stdin.resume();
process.stdin.setEncoding('utf-8');
let input = '';
process.stdin.on('data', (data) => {
    input += data;
    return;
});

const n = 64;
const dx = [-1, 0, 1, 0];
const dy = [0, 1, 0, -1];
// g 表示图形
const g = new Array(n).fill(0).map(() => new Array(n).fill(0));

process.stdin.on('end', () => {
    const lines = input.trim().split('\n');
    let index = 0;
    
    // nums 表示所有出现在 g 中的值
    const nums = [];

    const m = parseInt(lines[index++]);
    for (let i = 0; i < m; i++) {
        const s = lines[index++].trim().split(' ');
        const v = parseInt(s[0]);
        nums.push(v);
        for (let j = 1; j < s.length; j += 2) {
            const x = parseInt(s[j]);
            const y = parseInt(s[j + 1]);
            // 给对应的格子赋值
            g[x][y] = v;
        }
    }

    // 统计每个值的周长之和
    const val = new Map();
    for (let i = 0; i < n; i++) {
        for (let j = 0; j < n; j++) {
            // 当一个格子有值,才可能有边长
            if (g[i][j] > 0) {
                const v = g[i][j];
				let res = 4;
				for (let k = 0; k < 4; k++) {
					let nx = i + dx[k], ny = j + dy[k]
					if (nx >= 0 && nx < n && ny >= 0 && ny < n && g[nx][ny] === v) {
						res -= 1;
					}
				}
                val.set(v, (val.get(v) || 0) + res);
            }
        }
    }
    let res = '';
    for (let i = 0; i < m; i++) {
        res += val.get(nums[i]) + (i === m - 1 ? '\n' : ' ');
    }
    console.log(res);
});
  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

塔子哥学算法

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值