任意矩形图案解锁路径验证
华为OD笔试真题 5月27号 非AI方向 100分题型
题目内容
给定一个 M×NM \times NM×N 的矩形网格(M×N≤100M \times N \le 100M×N≤100),每个格子按照行优先顺序(从左到右,从上到下)依次填充正整数,起始值为 111。例如对于 3×33 \times 33×3 的网格:格子 (0,0)=1(0,0) = 1(0,0)=1,格子 (0,1)=2(0,1) = 2(0,1)=2,格子 (0,2)=3(0,2) = 3(0,2)=3,格子 (1,0)=4(1,0) = 4(1,0)=4,依此类推。

现输入一个整数序列,需判断该序列是否能构成网格中的一条有效解锁路径,需满足以下条件:
- 输入的整数序列的任意两个相邻的数字,在对应的网格中需要相邻。 网格中任意一个格子,其相邻的格子包括其上、下、左、右、左上、右上、左下、右下共 888 个方向的格子(若存在)。

注:数字 222 的相邻格子有 111、333、444、555、666;
注:数字 555 的相邻格子有 111、222、333、444、666、777、888、999。
- 序列中任意两个相邻数字组成的路径不能重复。
这里的路径重复是指包括正向和反向的。

以该网格为例,假如序列中出现了路径 (2,5)(2,5)(2,5),后续不能再出现路径 (2,5)(2,5)(2,5) 和 (5,2)(5,2)(5,2) 了。
举例1 ::整数序列 {1,2,3,5,7,81,2,3,5,7,81,2,3,5,7,8} 满足解锁条件,因为格子 111 和格子 222,格子 222 和格子 333,格子 333 和格子 555,格子 555 和格子 777,格子 777 和格子 888 相邻。

举例2 :整数序列 {1,2,3,4,71,2,3,4,71,2,3,4,7} 不满足解锁条件,因为格子 333 和格子 444 不相邻(标记 111)

举例3 :整数序列 {1,2,5,8,5,61,2,5,8,5,61,2,5,8,5,6} 不满足解锁条件,因为格子 555 和格子 888 构成的边与格子 888 与格子 555 构成的边重复了(标记 111 和标记 222)

输入描述
- 矩阵网格的行数 mmm(1≤m≤1001 \le m \le 1001≤m≤100)
- 矩阵网格的列数 nnn(1≤n≤1001 \le n \le 1001≤n≤100)
- 待解锁的序列个数 nnn(1≤n≤51 \le n \le 51≤n≤5)
- 待解锁的序列 sequencesequencesequence(1≤sequence≤m×n1 \le \text{sequence} \le m \times n1≤sequence≤m×n,sequencesequencesequence size\[\]size\[\]size\[\] 大于 111)
输出描述
- 输出所有可解锁的序列。
- 多个可解锁的序列按照输入的序列顺序输出,每个序列占一行。
- 如果一个序列都没有,返回 falsefalsefalse。
样例1
输入
3
3
4
1,2,5,8,5,6
1,2,3,4,7
1,2,3,5,7,8
1,2,3,5,7,4
输出
1,2,3,5,7,8
1,2,3,5,7,4
说明
有两个可解锁的序列,最终输出可解锁的序列。
1,2,3,5,7,81,2,3,5,7,81,2,3,5,7,8
1,2,3,5,7,41,2,3,5,7,41,2,3,5,7,4
样例2
输入
3
3
3
1,2,5,8,5,6
1,2,3,4,7
1,2,3,5,7,8
输出
1,2,3,5,7,8
说明
333 // 表示 333 行
333 // 表示 333 列
333 // 表示有 333 个待解锁的序列
整数序列 {1,2,5,8,5,61,2,5,8,5,61,2,5,8,5,6} 不满足解锁条件
整数序列 {1,2,3,4,71,2,3,4,71,2,3,4,7} 不满足解锁条件
整数序列 {1,2,3,5,7,8} 满足解锁条件,因为格子 111 和格子 222、格子 222 和格子 333、格子 333 和格子 555、格子 555 和格子 777、格子 777 和格子 888 均相邻。
因此,最终输出可解锁的序列 1,2,3,5,7,81,2,3,5,7,81,2,3,5,7,8
样例3
输入
3
3
1
1,2,5,8,5,6
1,2,3,4,7
输出
false
说明
整数序列 {1,2,5,8,5,61,2,5,8,5,61,2,5,8,5,6} 不满足解锁条件。
整数序列 {1,2,3,4,71,2,3,4,71,2,3,4,7} 不满足解锁条件,因为格子 333 和格子 444 不相邻。
由于这 222 个序列都不满足解锁条件,因此输出 falsefalsefalse。
样例4
输入
3
3
2
1,2,5,8,5,6
1,2,3,5,7,8
输出
1,2,3,5,7,8
说明
333 // 表示 333 行
333 // 表示 333 列
333 // 表示有 222 个序列待判断是否可解锁
1,2,5,8,5,61,2,5,8,5,61,2,5,8,5,6 // 表示待解锁的整数序列
1,2,3,5,7,81,2,3,5,7,81,2,3,5,7,8 // 表示待解锁的整数序列
整数序列 {1,2,5,8,5,61,2,5,8,5,61,2,5,8,5,6} 不满足解锁条件,因为格子 555 和格子 888 构成的边与格子 888 和格子 555 构成的边重复了。
整数序列 1,2,3,5,7,81,2,3,5,7,81,2,3,5,7,8 满足解锁条件。
故最终输出 1,2,3,5,7,81,2,3,5,7,81,2,3,5,7,8
题解和思路
思路
实现思路:模拟
输入的整数序列的任意两个相邻的数字,在对应的网格中需要相邻,检验这个规格,可以分别计算对应两个数字对应{ax,ay} 、{bx,by}横坐标和纵坐标。做如下检验:- 两点不属于同一位置
- 坐标未超过网格取余
- 对应横、纵坐标 差值绝对值都不大于1
- 不允许路径重复可以使用集合进行处理。
- 按照 1、 2对路径进项检验合法性,合法的路径保存在结果数组
- 结果数组为空输出
false,否则按顺序换行输出每个路径。
C++
cpp
#include<bits/stdc++.h>
using namespace std;
int m, n;
// 通用 切割函数 函数 将字符串str根据delimiter进行切割
vector<int> split(const string& str, const string& delimiter) {
vector<int> result;
size_t start = 0;
size_t end = str.find(delimiter);
while (end != string::npos) {
result.push_back(stoi(str.substr(start, end - start)));
start = end + delimiter.length();
end = str.find(delimiter, start);
}
// 添加最后一个部分
result.push_back(stoi(str.substr(start)));
return result;
}
bool judge(string& seqStr) {
vector<int> seq = split(seqStr, ",");
// 预防重复
set<pair<int,int>> s;
int l = seq.size();
for (int i = 0; i < l - 1; i++) {
// 统一转换为0-index
int a = seq[i] - 1;
int b = seq[i + 1] - 1;
if (a == b) {
return false;
}
// 分别计算对应横坐标、纵坐标
int ax = a / n, ay = a % n;
int bx = b / n, by = b % n;
// 超过边界
if (ax >= m || bx >= m) {
return false;
}
// 横纵坐标绝对值
if (abs(ax - bx) > 1 || abs(ay - by) > 1) {
return false;
}
// 路径重复
if (s.count({a, b}) || s.count({b, a})) {
return false;
}
s.insert({a, b});
}
return true;
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int count;
cin >> m;
cin >> n;
cin >> count;
cin.ignore();
vector<string> ans;
while (count--) {
string seq;
getline(cin, seq);
if (judge(seq)) {
ans.push_back(seq);
}
}
if (ans.empty()) {
cout << "false";
return 0;
}
for (int i = 0; i < ans.size(); i++) {
cout << ans[i] << endl;
}
}
Java
java
import java.io.*;
import java.util.*;
public class Main {
static int m, n;
static boolean judge(String seqStr) {
String[] arr = seqStr.split(",");
int[] seq = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
seq[i] = Integer.parseInt(arr[i]);
}
// 预防重复
Set<String> set = new HashSet<>();
int len = seq.length;
for (int i = 0; i < len - 1; i++) {
// 统一转换为0-index
int a = seq[i] - 1;
int b = seq[i + 1] - 1;
if (a == b) {
return false;
}
// 分别计算对应横坐标、纵坐标
int ax = a / n;
int ay = a % n;
int bx = b / n;
int by = b % n;
// 超过边界
if (ax >= m || bx >= m) {
return false;
}
// 横纵坐标绝对值
if (Math.abs(ax - bx) > 1 || Math.abs(ay - by) > 1) {
return false;
}
// 路径重复
String key1 = a + "#" + b;
String key2 = b + "#" + a;
if (set.contains(key1) || set.contains(key2)) {
return false;
}
set.add(key1);
}
return true;
}
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
m = Integer.parseInt(br.readLine());
n = Integer.parseInt(br.readLine());
int count = Integer.parseInt(br.readLine());
List<String> ans = new ArrayList<>();
while (count-- > 0) {
String seq = br.readLine();
if (judge(seq)) {
ans.add(seq);
}
}
if (ans.isEmpty()) {
System.out.print("false");
return;
}
for (String s : ans) {
System.out.println(s);
}
}
}
python
python
m = int(input())
n = int(input())
count = int(input())
def judge(seq_str):
seq = list(map(int, seq_str.split(",")))
# 预防重复
s = set()
length = len(seq)
for i in range(length - 1):
# 统一转换为0-index
a = seq[i] - 1
b = seq[i + 1] - 1
if a == b:
return False
# 分别计算对应横坐标、纵坐标
ax, ay = divmod(a, n)
bx, by = divmod(b, n)
# 超过边界
if ax >= m or bx >= m:
return False
# 横纵坐标绝对值
if abs(ax - bx) > 1 or abs(ay - by) > 1:
return False
# 路径重复
if (a, b) in s or (b, a) in s:
return False
s.add((a, b))
return True
ans = []
for _ in range(count):
seq = input().strip()
if judge(seq):
ans.append(seq)
if not ans:
print("false", end="")
else:
print("\n".join(ans), end="")
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.trim());
});
rl.on("close", () => {
let idx = 0;
const m = Number(lines[idx++]);
const n = Number(lines[idx++]);
const count = Number(lines[idx++]);
function judge(seqStr) {
const seq = seqStr.split(",").map(Number);
// 预防重复
const set = new Set();
const len = seq.length;
for (let i = 0; i < len - 1; i++) {
// 统一转换为0-index
const a = seq[i] - 1;
const b = seq[i + 1] - 1;
if (a === b) {
return false;
}
// 分别计算对应横坐标、纵坐标
const ax = Math.floor(a / n);
const ay = a % n;
const bx = Math.floor(b / n);
const by = b % n;
// 超过边界
if (ax >= m || bx >= m) {
return false;
}
// 横纵坐标绝对值
if (Math.abs(ax - bx) > 1 || Math.abs(ay - by) > 1) {
return false;
}
// 路径重复
const key1 = `${a},${b}`;
const key2 = `${b},${a}`;
if (set.has(key1) || set.has(key2)) {
return false;
}
set.add(key1);
}
return true;
}
const ans = [];
for (let i = 0; i < count; i++) {
const seq = lines[idx++];
if (judge(seq)) {
ans.push(seq);
}
}
if (ans.length === 0) {
process.stdout.write("false");
} else {
process.stdout.write(ans.join("\n"));
}
});
Go
go
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
var m, n int
func judge(seqStr string) bool {
arr := strings.Split(seqStr, ",")
seq := make([]int, len(arr))
for i, s := range arr {
seq[i], _ = strconv.Atoi(s)
}
// 预防重复
visited := make(map[[2]int]bool)
length := len(seq)
for i := 0; i < length-1; i++ {
// 统一转换为0-index
a := seq[i] - 1
b := seq[i+1] - 1
if a == b {
return false
}
// 分别计算对应横坐标、纵坐标
ax, ay := a/n, a%n
bx, by := b/n, b%n
// 超过边界
if ax >= m || bx >= m {
return false
}
// 横纵坐标绝对值
if abs(ax-bx) > 1 || abs(ay-by) > 1 {
return false
}
// 路径重复
if visited[[2]int{a, b}] || visited[[2]int{b, a}] {
return false
}
visited[[2]int{a, b}] = true
}
return true
}
func abs(x int) int {
if x < 0 {
return -x
}
return x
}
func main() {
reader := bufio.NewReader(os.Stdin)
fmt.Fscan(reader, &m)
fmt.Fscan(reader, &n)
var count int
fmt.Fscan(reader, &count)
reader.ReadString('\n')
var ans []string
for i := 0; i < count; i++ {
seq, _ := reader.ReadString('\n')
seq = strings.TrimSpace(seq)
if judge(seq) {
ans = append(ans, seq)
}
}
if len(ans) == 0 {
fmt.Print("false")
return
}
for _, s := range ans {
fmt.Println(s)
}
}