公交线路换乘优化(C++/Py/Java/Js/Go)题解
华为笔试真题 6月17号 非AI方向第一题 100分题型
题目内容
某城市有 NNN 条公交线路,每条线路用一个线性表示,包含该线路经过的所有站点(站点取值范围 1,20001,20001,2000),每条线路都是单向的,且站点顺序已按线路方向给出。
现在小明要从起点站A到终点站B,他可以选择,乘坐同一线路直达(如果A和B在同一条线路上)或最多换乘一次,换乘规则如下:
- 只能在两条线路的交汇站换乘。
- 起点站和终点站各算一次,换乘站只算一次。
- 起点不会等于终点,终点一定在起点之后。
请编写程序,计算从起点站点到终点站点需要经过的最少站点总数(包括起点和终点)。
输入描述
第一行:一个整数NNN,表示公交线路数量(1≤N≤201 \le N \le 201≤N≤20)
接下来NNN行,每行表示一条公交线路
每行第一个整数MMM表示该线路的站点数量(2≤M≤1002 \le M \le 1002≤M≤100)
后面MMM个整数表示公交线路经过的站点ID(按顺序)
最后一行:两个整数A,BA,BA,B,表示起点站点和终点站点
输出描述
一个整数,表示最少需要经过的站点数 如果无法到达,输出−1-1−1
样例1
输入
2
4 1 2 3 4
3 5 6 7
1 7
输出
-1
说明
线路111:111-222-333-444
线路222:555-666-777
没有交汇站,无法换乘,也无法直达
样例2
输入
3
5 1 2 3 4 5
4 3 6 7 8
3 5 9 10
1 8
输出
6
题解和思路
思路
实现思路:暴力枚举
- 题目只允许进行中转一次可以分析只会存在两种形式的方案
- 只使用单条线路
- 使用两条线路,线路之间存在中转点的情况,并且只能中转一次。
- 优先处理单条线路情况,线路必须存在终点和起点,并且起点位于终点之前。
- 枚举不同线路,其中线路A必须存在起点,线路B必须存在终点,枚举B终点之前的站点,如果在A起点之后存在该站点则可作为中转站,计算对应站点数量。
- 2、3中距离最小值就是结果。
算法时间复杂度为O(n^2 * m)
C++
cpp
#include<bits/stdc++.h>
using namespace std;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int N;
cin >> N;
vector<vector<int>> roads(N);
for (int i = 0; i < N; i++) {
int m;
cin >> m;
for (int j = 0; j < m; j++) {
int v;
cin >> v;
roads[i].push_back(v);
}
}
int start,end;
cin >> start >> end;
vector<unordered_map<int, int>> pos(N);
for (int i = 0; i < N; i++) {
for (int j = 0; j < roads[i].size(); j++) {
pos[i][roads[i][j]] = j;
}
}
int res = INT_MAX;
// 考虑单条线路情况
for (int i = 0; i < N; i++) {
unordered_map<int, int> p = pos[i];
if (p.count(start) && p.count(end) && p[start] < p[end]) {
cout << "Xxx";
res = min(res, p[end] - p[start] + 1);
}
}
// 考虑中转情况
for (int i = 0; i < N; i++) {
if (!pos[i].count(start)) {
continue;
}
int pStart = pos[i][start];
for (int j = 0; j < N; j++) {
// 只考虑不同线路
if (i == j) {
continue;
}
// 不包含终点直接跳过
if (!pos[j].count(end)) {
continue;
}
int pEnd = pos[j][end];
// 枚举换乘点,单项枚举j线路终点之前的站点
for (int k = pEnd - 1; k >= 0; k--) {
int transfer = roads[j][k];
// i必须存在中转站并且在起点之后
if (pos[i].count(transfer) && pos[i][transfer] > pStart) {
int cnt = (pEnd - k + 1) + (pos[i][transfer] - pStart + 1) - 1;
res = min(res, cnt);
}
}
}
}
if (res == INT_MAX) {
cout << -1;
return 0;
}
cout << res;
return 0;
}
Java
java
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws Exception {
Scanner sc = new Scanner(System.in);
int N = sc.nextInt();
List<List<Integer>> roads = new ArrayList<>();
for (int i = 0; i < N; i++) {
int m = sc.nextInt();
List<Integer> line = new ArrayList<>();
for (int j = 0; j < m; j++) {
line.add(sc.nextInt());
}
roads.add(line);
}
int start = sc.nextInt();
int end = sc.nextInt();
List<Map<Integer, Integer>> pos = new ArrayList<>();
for (int i = 0; i < N; i++) {
Map<Integer, Integer> map = new HashMap<>();
List<Integer> road = roads.get(i);
for (int j = 0; j < road.size(); j++) {
map.put(road.get(j), j);
}
pos.add(map);
}
int res = Integer.MAX_VALUE;
// 考虑单条线路情况
for (int i = 0; i < N; i++) {
Map<Integer, Integer> p = pos.get(i);
if (p.containsKey(start) && p.containsKey(end)
&& p.get(start) < p.get(end)) {
System.out.print("Xxx");
res = Math.min(res, p.get(end) - p.get(start) + 1);
}
}
// 考虑中转情况
for (int i = 0; i < N; i++) {
if (!pos.get(i).containsKey(start)) {
continue;
}
int pStart = pos.get(i).get(start);
for (int j = 0; j < N; j++) {
// 只考虑不同线路
if (i == j) {
continue;
}
// 不包含终点直接跳过
if (!pos.get(j).containsKey(end)) {
continue;
}
int pEnd = pos.get(j).get(end);
// 枚举换乘点,单项枚举j线路终点之前的站点
for (int k = pEnd - 1; k >= 0; k--) {
int transfer = roads.get(j).get(k);
// i必须存在中转站并且在起点之后
if (pos.get(i).containsKey(transfer)
&& pos.get(i).get(transfer) > pStart) {
int cnt =
(pEnd - k + 1)
+ (pos.get(i).get(transfer) - pStart + 1)
- 1;
res = Math.min(res, cnt);
}
}
}
}
if (res == Integer.MAX_VALUE) {
System.out.print(-1);
return;
}
System.out.print(res);
}
}
python
python
import sys
data = list(map(int, sys.stdin.read().split()))
idx = 0
N = data[idx]
idx += 1
roads = []
for _ in range(N):
m = data[idx]
idx += 1
road = data[idx:idx + m]
idx += m
roads.append(road)
start = data[idx]
idx += 1
end = data[idx]
pos = []
for road in roads:
mp = {}
for i, v in enumerate(road):
mp[v] = i
pos.append(mp)
res = float('inf')
# 考虑单条线路情况
for i in range(N):
p = pos[i]
if start in p and end in p and p[start] < p[end]:
print("Xxx", end="")
res = min(res, p[end] - p[start] + 1)
# 考虑中转情况
for i in range(N):
if start not in pos[i]:
continue
p_start = pos[i][start]
for j in range(N):
# 只考虑不同线路
if i == j:
continue
# 不包含终点直接跳过
if end not in pos[j]:
continue
p_end = pos[j][end]
# 枚举换乘点,单项枚举j线路终点之前的站点
for k in range(p_end - 1, -1, -1):
transfer = roads[j][k]
# i必须存在中转站并且在起点之后
if transfer in pos[i] and pos[i][transfer] > p_start:
cnt = (
(p_end - k + 1)
+ (pos[i][transfer] - p_start + 1)
- 1
)
res = min(res, cnt)
if res == float('inf'):
print(-1)
else:
print(res, end="")
Javascript
js
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
let input = [];
rl.on('line', line => {
input.push(line);
});
rl.on('close', () => {
const data = input.join(' ').trim().split(/\s+/).map(Number);
let idx = 0;
const N = data[idx++];
const roads = [];
for (let i = 0; i < N; i++) {
const m = data[idx++];
const road = [];
for (let j = 0; j < m; j++) {
road.push(data[idx++]);
}
roads.push(road);
}
const start = data[idx++];
const end = data[idx++];
const pos = [];
for (let i = 0; i < N; i++) {
const mp = new Map();
for (let j = 0; j < roads[i].length; j++) {
mp.set(roads[i][j], j);
}
pos.push(mp);
}
let res = Number.MAX_SAFE_INTEGER;
// 考虑单条线路情况
for (let i = 0; i < N; i++) {
const p = pos[i];
if (
p.has(start) &&
p.has(end) &&
p.get(start) < p.get(end)
) {
process.stdout.write("Xxx");
res = Math.min(
res,
p.get(end) - p.get(start) + 1
);
}
}
// 考虑中转情况
for (let i = 0; i < N; i++) {
if (!pos[i].has(start)) {
continue;
}
const pStart = pos[i].get(start);
for (let j = 0; j < N; j++) {
// 只考虑不同线路
if (i === j) {
continue;
}
// 不包含终点直接跳过
if (!pos[j].has(end)) {
continue;
}
const pEnd = pos[j].get(end);
// 枚举换乘点,单项枚举j线路终点之前的站点
for (let k = pEnd - 1; k >= 0; k--) {
const transfer = roads[j][k];
// i必须存在中转站并且在起点之后
if (
pos[i].has(transfer) &&
pos[i].get(transfer) > pStart
) {
const cnt =
(pEnd - k + 1) +
(pos[i].get(transfer) - pStart + 1) -
1;
res = Math.min(res, cnt);
}
}
}
}
if (res === Number.MAX_SAFE_INTEGER) {
console.log(-1);
return;
}
console.log(res);
});
Go
go
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
in := bufio.NewReader(os.Stdin)
var N int
fmt.Fscan(in, &N)
roads := make([][]int, N)
for i := 0; i < N; i++ {
var m int
fmt.Fscan(in, &m)
roads[i] = make([]int, m)
for j := 0; j < m; j++ {
fmt.Fscan(in, &roads[i][j])
}
}
var start, end int
fmt.Fscan(in, &start, &end)
pos := make([]map[int]int, N)
for i := 0; i < N; i++ {
pos[i] = make(map[int]int)
for j, v := range roads[i] {
pos[i][v] = j
}
}
res := int(^uint(0) >> 1)
// 考虑单条线路情况
for i := 0; i < N; i++ {
p := pos[i]
startPos, ok1 := p[start]
endPos, ok2 := p[end]
if ok1 && ok2 && startPos < endPos {
fmt.Print("Xxx")
if endPos-startPos+1 < res {
res = endPos - startPos + 1
}
}
}
// 考虑中转情况
for i := 0; i < N; i++ {
pStart, ok := pos[i][start]
if !ok {
continue
}
for j := 0; j < N; j++ {
// 只考虑不同线路
if i == j {
continue
}
// 不包含终点直接跳过
pEnd, ok := pos[j][end]
if !ok {
continue
}
// 枚举换乘点,单项枚举j线路终点之前的站点
for k := pEnd - 1; k >= 0; k-- {
transfer := roads[j][k]
// i必须存在中转站并且在起点之后
if transferPos, ok := pos[i][transfer]; ok && transferPos > pStart {
cnt :=
(pEnd-k+1) +
(transferPos-pStart+1) -
1
if cnt < res {
res = cnt
}
}
}
}
}
if res == int(^uint(0)>>1) {
fmt.Print(-1)
return
}
fmt.Print(res)
}