字节跳动笔试真题 - 矩阵印章染色(C++/Py/Java/Js/Go)

矩阵印章染色

字节跳动笔试真题 4月19号 第二题

题目内容

小红拿到了一个矩阵,nnn 行 mmm 列共 n×mn \times mn×m 个小方格。小红有一个 2×22 \times 22×2 的印章,每次可以将一个2×22 \times 22×2的子矩阵染成红色,每个格子只能被染一次。小红染了若干个红色的 2×22 \times 22×2 块,并且最终保证了每个 2×22 \times 22×2 红色块都是不相邻的(如果两个红色块共用了同一个边或者同一个角,则称为两个红色块相邻)。

例如:下面矩阵为非法的,因为两个 2×22 \times 22×2 红色块相邻了(共用了同一个角):

∗∗..**..∗∗..

∗∗..**..∗∗..

..∗∗..**..∗∗

..∗∗..**..∗∗

小红忘了自己的染色过程,她拿到了一个矩阵,她想知道这个矩阵是否是按她的要求染色的?

共有 ttt 组询问。

输入描述

第一行输入一个正整数 ttt,代表询问的次数。

对于每组询问,先输入两个正整数 nnn 和 mmm,代表矩阵的行数和列数。

接下来的 nnn 行,每行输入一个长度为 mmm 的字符串。字符*代表格子被染成红色,.代表未被染色。

1≤t,n,m≤501 \le t, n, m \le 501≤t,n,m≤50

输出描述

输出 ttt 行,每行对应一组询问的答案。

若该图形是小红染色的,则输出 "YesYesYes"。否则输出"NoNoNo"。

样例1

输入

复制代码
3
4 4
**..
**..
..**
..**
4 5
**...
**...
...**
...**
2 2
*.
..

输出

复制代码
No
Yes
No

题解

思路

模拟

  1. 按照题目思路进行模拟实现即可。
  2. 定义vis数组记录某个位置是否被访问,按照从上往下、从左往右方向进行遍历,如果遇到grid[i][j] == '**' and vis[i][j] = false把这个位置当作印章的左上角,并进行合法性判断
    • 当前位置是否允许构成2 * 2区域
    • 构成的2 * 2区域全为红色
    • 包裹当前2 * 2区域的4 * 4区域最外边是否都不为*
    • 上述任意不满足返回false
  3. 代码整体复杂度为O(n * m)

C++

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;


bool check(vector<vector<char>>& grid) {
    int n = grid.size();
    int m = grid[0].size();
    vector<vector<bool>> vis(n, vector<bool>(m, false));
    
    // 枚举所有*,未vis被当作2*2的左上角
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
                // 访问过或者不是染色直接跳过
                if (vis[i][j] || grid[i][j] != '*') {
                    continue;
                }
                // 不能构成完整2 *  2
                if (i + 1 >= n || j + 1 >= m) {
                    return false;
                }
            
                // 不能构成2*2染色区域
                if (grid[i][j+1] != '*' || grid[i+1][j] != '*' || grid[i+1][j+1] != '*') {
                    return false;
                }
                // 更新为已经访问
                vis[i][j] = vis[i][j+1] = vis[i+1][j] = vis[i+1][j+1] = true;
                // 检查是否存在与当前 2 * 2相邻的染色
                for (int x = i -1; x <= i + 2; x++) {
                    for (int y = j -1; y <= j + 2; y++) {
                        if (x < 0 || x >= n || y < 0 || y >= m) {
                            continue;
                        }
                        if (x >= i && x <= i + 1 && y >= j && y <= j + 1) {
                            continue;
                        }
                        // 相邻区域被染色
                        if (grid[x][y] == '*')  {
                            return false;
                        }
                    }
                }
                
        }
    }
    return true;
}


int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int T;
    cin >> T;
    while (T--) {
        int n, m;
        cin >> n >> m;
        vector<vector<char>> grid(n, vector<char>(m));
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                cin >> grid[i][j];         
            }
        }
        if (check(grid)) {
            cout << "Yes" << endl;
        } else {
            cout << "No" << endl;
        }
    }
    return 0;
}

java

java 复制代码
import java.io.*;
import java.util.*;

public class Main {

    static boolean check(char[][] grid) {
        int n = grid.length;
        int m = grid[0].length;

        boolean[][] vis = new boolean[n][m];

        // 枚举所有*,未vis被当作2*2的左上角
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {

                // 访问过或者不是染色直接跳过
                if (vis[i][j] || grid[i][j] != '*') {
                    continue;
                }

                // 不能构成完整2 * 2
                if (i + 1 >= n || j + 1 >= m) {
                    return false;
                }

                // 不能构成2*2染色区域
                if (grid[i][j + 1] != '*' ||
                    grid[i + 1][j] != '*' ||
                    grid[i + 1][j + 1] != '*') {
                    return false;
                }

                // 更新为已经访问
                vis[i][j] = true;
                vis[i][j + 1] = true;
                vis[i + 1][j] = true;
                vis[i + 1][j + 1] = true;

                // 检查是否存在与当前 2 * 2相邻的染色
                for (int x = i - 1; x <= i + 2; x++) {
                    for (int y = j - 1; y <= j + 2; y++) {

                        if (x < 0 || x >= n || y < 0 || y >= m) {
                            continue;
                        }

                        if (x >= i && x <= i + 1 &&
                            y >= j && y <= j + 1) {
                            continue;
                        }

                        // 相邻区域被染色
                        if (grid[x][y] == '*') {
                            return false;
                        }
                    }
                }
            }
        }

        return true;
    }

    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        int T = Integer.parseInt(br.readLine());

        while (T-- > 0) {
            String[] first = br.readLine().split(" ");

            int n = Integer.parseInt(first[0]);
            int m = Integer.parseInt(first[1]);

            char[][] grid = new char[n][m];

            for (int i = 0; i < n; i++) {
                String row = br.readLine();

                for (int j = 0; j < m; j++) {
                    grid[i][j] = row.charAt(j);
                }
            }

            if (check(grid)) {
                System.out.println("Yes");
            } else {
                System.out.println("No");
            }
        }
    }
}

python

python 复制代码
import sys


def check(grid):
    n = len(grid)
    m = len(grid[0])

    vis = [[False] * m for _ in range(n)]

    # 枚举所有*,未vis被当作2*2的左上角
    for i in range(n):
        for j in range(m):

            # 访问过或者不是染色直接跳过
            if vis[i][j] or grid[i][j] != '*':
                continue

            # 不能构成完整2 * 2
            if i + 1 >= n or j + 1 >= m:
                return False

            # 不能构成2*2染色区域
            if (grid[i][j + 1] != '*' or
                grid[i + 1][j] != '*' or
                grid[i + 1][j + 1] != '*'):
                return False

            # 更新为已经访问
            vis[i][j] = True
            vis[i][j + 1] = True
            vis[i + 1][j] = True
            vis[i + 1][j + 1] = True

            # 检查是否存在与当前 2 * 2相邻的染色
            for x in range(i - 1, i + 3):
                for y in range(j - 1, j + 3):

                    if x < 0 or x >= n or y < 0 or y >= m:
                        continue

                    if i <= x <= i + 1 and j <= y <= j + 1:
                        continue

                    # 相邻区域被染色
                    if grid[x][y] == '*':
                        return False

    return True


data = sys.stdin.read().strip().splitlines()
idx = 0

T = int(data[idx])
idx += 1

ans = []

for _ in range(T):
    n, m = map(int, data[idx].split())
    idx += 1

    grid = []

    for _ in range(n):
        grid.append(list(data[idx]))
        idx += 1

    if check(grid):
        ans.append("Yes")
    else:
        ans.append("No")

print("\n".join(ans))

javascript

js 复制代码
const readline = require('readline');

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

const lines = [];

rl.on('line', line => {
    lines.push(line);
});

rl.on('close', () => {
    let idx = 0;

    function check(grid) {
        const n = grid.length;
        const m = grid[0].length;

        const vis = Array.from(
            { length: n },
            () => Array(m).fill(false)
        );

        // 枚举所有*,未vis被当作2*2的左上角
        for (let i = 0; i < n; i++) {
            for (let j = 0; j < m; j++) {

                // 访问过或者不是染色直接跳过
                if (vis[i][j] || grid[i][j] !== '*') {
                    continue;
                }

                // 不能构成完整2 * 2
                if (i + 1 >= n || j + 1 >= m) {
                    return false;
                }

                // 不能构成2*2染色区域
                if (
                    grid[i][j + 1] !== '*' ||
                    grid[i + 1][j] !== '*' ||
                    grid[i + 1][j + 1] !== '*'
                ) {
                    return false;
                }

                // 更新为已经访问
                vis[i][j] = true;
                vis[i][j + 1] = true;
                vis[i + 1][j] = true;
                vis[i + 1][j + 1] = true;

                // 检查是否存在与当前 2 * 2相邻的染色
                for (let x = i - 1; x <= i + 2; x++) {
                    for (let y = j - 1; y <= j + 2; y++) {

                        if (x < 0 || x >= n || y < 0 || y >= m) {
                            continue;
                        }

                        if (
                            x >= i && x <= i + 1 &&
                            y >= j && y <= j + 1
                        ) {
                            continue;
                        }

                        // 相邻区域被染色
                        if (grid[x][y] === '*') {
                            return false;
                        }
                    }
                }
            }
        }

        return true;
    }

    const T = Number(lines[idx++]);

    const ans = [];

    for (let t = 0; t < T; t++) {
        const [n, m] = lines[idx++].split(' ').map(Number);

        const grid = [];

        for (let i = 0; i < n; i++) {
            grid.push(lines[idx++].split(''));
        }

        ans.push(check(grid) ? 'Yes' : 'No');
    }

    console.log(ans.join('\n'));
});

Go

go 复制代码
package main

import (
	"bufio"
	"fmt"
	"os"
)

func check(grid [][]byte) bool {
	n := len(grid)
	m := len(grid[0])

	vis := make([][]bool, n)
	for i := 0; i < n; i++ {
		vis[i] = make([]bool, m)
	}

	// 枚举所有*,未vis被当作2*2的左上角
	for i := 0; i < n; i++ {
		for j := 0; j < m; j++ {

			// 访问过或者不是染色直接跳过
			if vis[i][j] || grid[i][j] != '*' {
				continue
			}

			// 不能构成完整2 * 2
			if i+1 >= n || j+1 >= m {
				return false
			}

			// 不能构成2*2染色区域
			if grid[i][j+1] != '*' ||
				grid[i+1][j] != '*' ||
				grid[i+1][j+1] != '*' {
				return false
			}

			// 更新为已经访问
			vis[i][j] = true
			vis[i][j+1] = true
			vis[i+1][j] = true
			vis[i+1][j+1] = true

			// 检查是否存在与当前 2 * 2相邻的染色
			for x := i - 1; x <= i+2; x++ {
				for y := j - 1; y <= j+2; y++ {

					if x < 0 || x >= n || y < 0 || y >= m {
						continue
					}

					if x >= i && x <= i+1 &&
						y >= j && y <= j+1 {
						continue
					}

					// 相邻区域被染色
					if grid[x][y] == '*' {
						return false
					}
				}
			}
		}
	}

	return true
}

func main() {
	in := bufio.NewReader(os.Stdin)

	var T int
	fmt.Fscan(in, &T)

	for ; T > 0; T-- {
		var n, m int
		fmt.Fscan(in, &n, &m)

		grid := make([][]byte, n)

		for i := 0; i < n; i++ {
			var row string
			fmt.Fscan(in, &row)
			grid[i] = []byte(row)
		}

		if check(grid) {
			fmt.Println("Yes")
		} else {
			fmt.Println("No")
		}
	}
}