直捣黄龙
华为OD机试真题 华为OD上机考试真题 4月8号 200分题型
华为OD机试真题目录点击查看: 华为OD机试真题题库目录|机考题库 + 算法考点详解
题目描述
小王在玩一款叫做直捣黄龙的小游戏,在该游戏中他需要从入口位置进入敌营,绕过哨兵的层层封锁,达到敌军司令部实施斩首行动。
敌军阵营是一个 n∗n 的矩阵,入口在坐标 (0,n/2),敌军司令部在坐标 (n−1,n/2),每个哨兵警戒以自己为中心的9宫格,一旦被哨兵发现则行动失败。同时穿越敌营耗时越长,被发现的概率越高,因此小王需要寻找到可以绕过警戒到达敌军司令部的最短路径。请你设计一个小程序,帮助小王统计这样的路径有多少条,以及路径长度。
规则说明:
- 其中 n为大于 1的奇数且取值小于 30 ,坐标 x,y 取值均从 0 开始,敌营左下角定义为 (0,0),右上角定义为 (n−1,n−1)
- 敌营入口在坐标 (0,n/2),敌军司令部在坐标 (n−1,n/2)。
- 游戏角色的行动方向只包含上、下、左、右四个方向,即一次行动 x、y坐标不可同时变化。
- 在没有满足题目要求的可达路径时,需要返回0 0。
输入描述
参数 1,敌军阵营的边长 n 。
参数 2,哨兵位置列表 Point{x,y}x未行坐标,y为列坐标。x和y以逗号分割,不同坐标以空格分割
输出描述
输出两个成员空格分割,第一个成员为最短路径条数,第二个成员为最短路径长度。
用例1
输入
none
5
2,2
输出
none
2 9
说明
两条最短路径,S 表示哨兵位置,A 表示起点,E 表示终点
路径1:[(0,2),(0,1),(0,0),(1,0),(2,0),(3,0),(4,0),(4,1),(4,2)]
路径2:[(0,2),(0,3),(0,4),(1,4),(2,4),(3,4),(4,4),(4,3),(4,2)]
因此返回为2 9
用例2
输入
none
3
1,1
输出
none
0 0
说明
无路径场景,S 表示哨兵位置,A 表示起点,E 表示终点,哨兵警戒了全图
用例3
输入
5
2,1
输出
1 7
说明
单一最短路径场景,S 表示哨兵位置,A表示起点,E表示终点
最短路径:[(0,2),(0,3),(1,3),(2,3),(3,3),(4,3),(4,2)]
题解
思路:BFS
- 求最短路径和最短路径方案数,可以借助
dist(到达此处最少节点数)和distNum(到达此处为最少节点的方案)数组使用BFS进行求解。 - 预处理不可访问位置,哨兵位置{x,y}对应九宫格不可访问就是
[x-1 - x +1][y-1 - y+1]九个位置不可访问,可以标记为-1 - 接下来就是使用BFS算法进行处理,状态转移过程为,当前位置为
x, y,扩展到nx,ny时- 如果
nx,ny未访问,则更新dist[nx][ny] = dist[x][y] + 1,distNum[nx][ny] = distNum[x][y]并入队 - 如果
nx,ny以访问,并且dist[x][y] + 1 == dist[nx][ny]情况下,说明又找到一条路径,更新distNum[nx][ny] += distNum[x][y]
- 如果
- 按照3逻辑之后,最终判断
dist[ex][ey]是否能访问到?不能访问返回{0,0}否则范围对应值即可
额外注意最短路径是访问节点数,所以初始应该设置
dist[ex][ey] =1
c++
c++
#include<iostream>
#include<vector>
#include<string>
#include <utility>
#include <sstream>
#include<algorithm>
#include<cmath>
#include<map>
#include<climits>
#include <queue>
using namespace std;
struct Point {
int x;
int y;
Point(int xx, int yy) : x(xx), y(yy) {}
};
// 通用 切割函数 函数 将字符串str根据delimiter进行切割
vector<string> split(const string& str, const string& delimiter) {
vector<string> result;
size_t start = 0;
size_t end = str.find(delimiter);
while (end != string::npos) {
result.push_back(str.substr(start, end - start));
start = end + delimiter.length();
end = str.find(delimiter, start);
}
// 添加最后一个部分
result.push_back(str.substr(start));
return result;
}
// n大小 points哨兵位置
vector<int> calShortestPath(int n, vector<Point>& points) {
vector<vector<int>> grid(n, vector<int>(n, 0));
// 标记不可达位置
for (Point& point : points) {
int x = point.x;
int y = point.y;
for (int i = x - 1; i <= x + 1; i++) {
for (int j = y -1; j <= y + 1; j++) {
if (i < 0 || i >= n || j < 0 || j >=n) {
continue;
}
grid[i][j] = -1;
}
}
}
int dirs[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
int sx = 0, sy = n / 2;
int ex = n - 1, ey = n / 2;
// 特殊情况判断
if (grid[sx][sy] == -1 || grid[ex][ey] == -1) {
return {0, 0};
}
// dist 记录最少节点数,-1 表示未访问
vector<vector<int>> dist(n, vector<int>(n, -1));
// distNum 最少节点数到达次数方案数
vector<vector<int>> distNum(n, vector<int>(n, 0));
queue<pair<int, int>> q;
q.push({sx, sy});
dist[sx][sy] = 1;
distNum[sx][sy] = 1;
while (!q.empty()) {
auto p = q.front();
q.pop();
int x = p.first, y = p.second;
for (int i = 0; i < 4; i++) {
int nx = x + dirs[i][0];
int ny = y + dirs[i][1];
// 越界或被监控
if (nx < 0 || nx >= n || ny < 0 || ny >= n || grid[nx][ny] == -1) {
continue;
}
// 初次访问
if (dist[nx][ny] == -1) {
// 步数+1
dist[nx][ny] = dist[x][y] + 1;
// 数量相同
distNum[nx][ny] = distNum[x][y];
q.push({nx, ny});
} else if (dist[x][y] + 1 == dist[nx][ny]) {
distNum[nx][ny] += distNum[x][y];
}
}
}
// 不可达
if (dist[ex][ey] == -1) {
return {0, 0};
}
return {distNum[ex][ey], dist[ex][ey]};
}
int main() {
int n;
cin >> n;
// 吃掉换行
cin.ignore();
string line;
getline(cin, line);
vector<Point> points;
for (auto &s : split(line, " ")) {
vector<string> rowCol = split(s, ",");
points.push_back({stoi(rowCol[0]), stoi(rowCol[1])});
}
vector<int> res = calShortestPath(n, points);
cout << res[0] << " " << res[1];
return 0;
}
JAVA
JAVA
import java.util.*;
class Point {
int x, y;
Point(int xx, int yy) { x = xx; y = yy; }
}
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
sc.nextLine(); // 吃掉换行
String line = sc.nextLine();
List<Point> points = new ArrayList<>();
for (String s : line.split(" ")) {
String[] rowCol = s.split(",");
points.add(new Point(Integer.parseInt(rowCol[0]), Integer.parseInt(rowCol[1])));
}
int[] res = calShortestPath(n, points);
System.out.println(res[0] + " " + res[1]);
}
static int[] calShortestPath(int n, List<Point> points) {
int[][] grid = new int[n][n];
// 标记不可达位置
for (Point point : points) {
int x = point.x;
int y = point.y;
for (int i = x - 1; i <= x + 1; i++) {
for (int j = y - 1; j <= y + 1; j++) {
if (i < 0 || i >= n || j < 0 || j >= n) continue;
grid[i][j] = -1;
}
}
}
int[][] dirs = {{1,0},{-1,0},{0,1},{0,-1}};
int sx = 0, sy = n/2;
int ex = n-1, ey = n/2;
// 特殊情况判断
if (grid[sx][sy] == -1 || grid[ex][ey] == -1) return new int[]{0,0};
// dist 记录节点数,-1 表示未访问
int[][] dist = new int[n][n];
// distNum 最少节点数到达次数方案数
int[][] distNum = new int[n][n];
for (int[] row : dist) Arrays.fill(row, -1);
Queue<int[]> q = new LinkedList<>();
q.offer(new int[]{sx, sy});
dist[sx][sy] = 1;
distNum[sx][sy] = 1;
while (!q.isEmpty()) {
int[] p = q.poll();
int x = p[0], y = p[1];
for (int[] d : dirs) {
int nx = x + d[0];
int ny = y + d[1];
// 越界或被监控
if (nx < 0 || nx >= n || ny < 0 || ny >= n || grid[nx][ny] == -1) continue;
// 初次访问
if (dist[nx][ny] == -1) {
dist[nx][ny] = dist[x][y] + 1;
// 数量相同
distNum[nx][ny] = distNum[x][y];
q.offer(new int[]{nx, ny});
} else if (dist[x][y] + 1 == dist[nx][ny]) {
distNum[nx][ny] += distNum[x][y];
}
}
}
// 不可达
if (dist[ex][ey] == -1) return new int[]{0,0};
return new int[]{distNum[ex][ey], dist[ex][ey]};
}
}
Python
python
from collections import deque
n = int(input())
line = input()
points = []
for s in line.split():
x, y = map(int, s.split(','))
points.append((x, y))
def calShortestPath(n, points):
grid = [[0]*n for _ in range(n)]
# 标记不可达位置
for x, y in points:
for i in range(x-1, x+2):
for j in range(y-1, y+2):
if 0 <= i < n and 0 <= j < n:
grid[i][j] = -1
dirs = [(1,0),(-1,0),(0,1),(0,-1)]
sx, sy = 0, n//2
ex, ey = n-1, n//2
# 特殊情况判断
if grid[sx][sy] == -1 or grid[ex][ey] == -1:
return 0, 0
# dist 记录最少节点数,-1 表示未访问
dist = [[-1]*n for _ in range(n)]
# distNum 最少节点数到达次数方案数
distNum = [[0]*n for _ in range(n)]
q = deque()
q.append((sx, sy))
dist[sx][sy] = 1
distNum[sx][sy] = 1
while q:
x, y = q.popleft()
for dx, dy in dirs:
nx, ny = x+dx, y+dy
# 越界或被监控
if 0 <= nx < n and 0 <= ny < n and grid[nx][ny] != -1:
# 初次访问
if dist[nx][ny] == -1:
dist[nx][ny] = dist[x][y] + 1
# 数量相同
distNum[nx][ny] = distNum[x][y]
q.append((nx, ny))
# 相同步数,方案累加
elif dist[x][y] + 1 == dist[nx][ny]:
distNum[nx][ny] += distNum[x][y]
# 不可达
if dist[ex][ey] == -1:
return 0, 0
return distNum[ex][ey], dist[ex][ey]
res = calShortestPath(n, points)
print(res[0], res[1])
JavaScript
js
const readline = require('readline');
const rl = readline.createInterface({ input: process.stdin });
let lines = [];
rl.on('line', line => {
lines.push(line);
}).on('close', () => {
const n = parseInt(lines[0]);
const points = [];
lines[1].split(' ').forEach(s => {
const [x, y] = s.split(',').map(Number);
points.push({x, y});
});
const res = calShortestPath(n, points);
console.log(res[0], res[1]);
});
function calShortestPath(n, points) {
const grid = Array.from({length:n}, () => Array(n).fill(0));
// 标记不可达位置
for (const p of points) {
for (let i = p.x-1; i <= p.x+1; i++) {
for (let j = p.y-1; j <= p.y+1; j++) {
if (i>=0 && i<n && j>=0 && j<n) grid[i][j] = -1;
}
}
}
const dirs = [[1,0],[-1,0],[0,1],[0,-1]];
const sx = 0, sy = Math.floor(n/2);
const ex = n-1, ey = Math.floor(n/2);
// 特殊情况判断
if (grid[sx][sy] === -1 || grid[ex][ey] === -1) return [0,0];
const dist = Array.from({length:n}, ()=>Array(n).fill(-1));
const distNum = Array.from({length:n}, ()=>Array(n).fill(0));
const q = [[sx, sy]];
dist[sx][sy] = 1;
distNum[sx][sy] = 1;
while (q.length) {
const [x, y] = q.shift();
for (const [dx, dy] of dirs) {
const nx = x+dx, ny = y+dy;
// 越界或被监控
if (nx<0||nx>=n||ny<0||ny>=n||grid[nx][ny]==-1) continue;
// 初次访问
if (dist[nx][ny]==-1) {
dist[nx][ny] = dist[x][y]+1;
distNum[nx][ny] = distNum[x][y];
q.push([nx, ny]);
} else if (dist[x][y]+1==dist[nx][ny]) {
// 相同步数,方案累加
distNum[nx][ny] += distNum[x][y];
}
}
}
// 不可达
if (dist[ex][ey]==-1) return [0,0];
return [distNum[ex][ey], dist[ex][ey]];
}
Go
go
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
type Point struct { x, y int }
func main() {
reader := bufio.NewReader(os.Stdin)
line1, _ := reader.ReadString('\n')
n, _ := strconv.Atoi(strings.TrimSpace(line1))
line2, _ := reader.ReadString('\n')
line2 = strings.TrimSpace(line2)
points := []Point{}
for _, s := range strings.Split(line2, " ") {
rc := strings.Split(s, ",")
x, _ := strconv.Atoi(rc[0])
y, _ := strconv.Atoi(rc[1])
points = append(points, Point{x, y})
}
res := calShortestPath(n, points)
fmt.Println(res[0], res[1])
}
func calShortestPath(n int, points []Point) [2]int {
grid := make([][]int, n)
for i := 0; i < n; i++ { grid[i] = make([]int, n) }
// 标记不可达位置
for _, p := range points {
for i := p.x-1; i <= p.x+1; i++ {
for j := p.y-1; j <= p.y+1; j++ {
if i>=0 && i<n && j>=0 && j<n {
grid[i][j] = -1
}
}
}
}
dirs := [][2]int{{1,0},{-1,0},{0,1},{0,-1}}
sx, sy := 0, n/2
ex, ey := n-1, n/2
// 特殊情况判断
if grid[sx][sy]==-1 || grid[ex][ey]==-1 { return [2]int{0,0} }
dist := make([][]int, n)
distNum := make([][]int, n)
for i := 0; i < n; i++ {
dist[i] = make([]int, n)
distNum[i] = make([]int, n)
for j := 0; j < n; j++ { dist[i][j] = -1 }
}
queue := [][2]int{{sx, sy}}
dist[sx][sy] = 1
distNum[sx][sy] = 1
for len(queue) > 0 {
cur := queue[0]; queue = queue[1:]
x, y := cur[0], cur[1]
for _, d := range dirs {
nx, ny := x+d[0], y+d[1]
// 越界或被监控
if nx<0 || nx>=n || ny<0 || ny>=n || grid[nx][ny]==-1 { continue }
// 初次访问
if dist[nx][ny]==-1 {
dist[nx][ny] = dist[x][y]+1
distNum[nx][ny] = distNum[x][y]
queue = append(queue, [2]int{nx, ny})
} else if dist[x][y]+1 == dist[nx][ny] {
// 相同步数,方案累加
distNum[nx][ny] += distNum[x][y]
}
}
}
// 不可达
if dist[ex][ey]==-1 { return [2]int{0,0} }
return [2]int{distNum[ex][ey], dist[ex][ey]}
}
C语言
cpp
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct { int x, y; } Point;
int main() {
int n;
scanf("%d\n", &n);
char line[10000];
fgets(line, sizeof(line), stdin);
Point points[500];
int pointCount = 0;
// 通用切割,根据空格分割坐标点
char *token = strtok(line, " \n");
while (token) {
int x, y;
sscanf(token, "%d,%d", &x, &y);
points[pointCount++] = (Point){x, y};
token = strtok(NULL, " \n");
}
int grid[50][50] = {0};
// 标记不可达位置
for (int k=0; k<pointCount; k++) {
int x = points[k].x, y = points[k].y;
for (int i=x-1;i<=x+1;i++)
for (int j=y-1;j<=y+1;j++)
if (i>=0 && i<n && j>=0 && j<n)
grid[i][j] = -1;
}
int dirs[4][2] = {{1,0},{-1,0},{0,1},{0,-1}};
int sx=0, sy=n/2, ex=n-1, ey=n/2;
// 特殊情况判断
if (grid[sx][sy]==-1 || grid[ex][ey]==-1) { printf("0 0\n"); return 0; }
int dist[50][50], distNum[50][50];
for(int i=0;i<n;i++)
for(int j=0;j<n;j++){ dist[i][j]=-1; distNum[i][j]=0; }
typedef struct { int x, y; } Node;
Node queue[2500]; int head=0, tail=0;
queue[tail++] = (Node){sx, sy};
dist[sx][sy] = 1;
distNum[sx][sy] = 1;
while(head<tail){
Node cur = queue[head++];
int x=cur.x, y=cur.y;
for(int d=0; d<4; d++){
int nx=x+dirs[d][0], ny=y+dirs[d][1];
// 越界或被监控
if(nx<0||nx>=n||ny<0||ny>=n||grid[nx][ny]==-1) continue;
// 初次访问
if(dist[nx][ny]==-1){
dist[nx][ny] = dist[x][y]+1;
distNum[nx][ny] = distNum[x][y];
queue[tail++] = (Node){nx, ny};
} else if(dist[x][y]+1==dist[nx][ny]){
// 相同步数,方案累加
distNum[nx][ny] += distNum[x][y];
}
}
}
// 不可达
if(dist[ex][ey]==-1) printf("0 0\n");
else printf("%d %d\n", distNum[ex][ey], dist[ex][ey]);
return 0;
}