限次罐笼
拼多多 9月6号 笔试真题 第三题
拼多多真题目录点击查看: 拼多多 春招&秋招 笔试真题题库目录|笔试题库 + 算法考点详解
题目内容
井下调度室要把一名巡检员从指定工位送到另一个工位。巷道里共有 n n n 个工位,编号从 0 到 n − 1 n-1 n−1。工位之间有两类通路:巷道需要按长度耗时行走;罐笼瞬时到达,但受安全规程限制。规程要求:全程最多乘坐 c c c 次罐笼;两次罐笼之间必须至少走过一条巷道,不能连坐;从起点出发时可以直接先坐罐笼。请计算到达目标的最短耗时;若起点与终点是同一工位,耗时为 0;若无法到达,输出 -1。
巷道是双向的,第 i i i 条连接工位 x i x_i xi 与 y i y_i yi,耗时为 d i d_i di( d i ≥ ' 0 ' d_i\ge `0` di≥'0')。罐笼也是双向的,第 j j j 条连接工位 a j a_j aj 与 b j b_j bj,耗时为 0。巷道和罐笼都可能出现重边或自环。
约束:
1≤ \le ≤ n n n ≤ \le ≤ 10 4 10^4 1040≤ \le ≤ q q q ≤ \le ≤ 2 × 10 5 2 \times 10^5 2×1050≤ \le ≤ b b b ≤ \le ≤100≤ \le ≤ c c c ≤ \le ≤100≤ \le ≤ d i d_i di ≤ \le ≤10000000000≤ \le ≤ s , t s,t s,t < < < n n n
输入描述
第一行两个整数 n n n 和 q q q(1 ≤ \le ≤ n n n ≤ \le ≤ 10 4 10^4 104,0 ≤ \le ≤ q q q ≤ \le ≤ 2 × 10 5 2 \times 10^5 2×105),表示工位数和巷道条数。
接下来 q q q 行,每行三个整数 x x x、 y y y、 d d d(0 ≤ \le ≤ x , y x,y x,y < < < n n n,0 ≤ \le ≤ d d d ≤ \le ≤ 1000000000),表示一条双向巷道。
下一行一个整数 b b b(0 ≤ \le ≤ b b b ≤ \le ≤ 10),表示罐笼条数。
接下来 b b b 行,每行两个整数 a a a、 f f f(0 ≤ \le ≤ a , f a,f a,f < < < n n n),表示一条双向罐笼。
最后一行三个整数 c c c、 s s s、 t t t(0 ≤ \le ≤ c c c ≤ \le ≤ 10,0 ≤ \le ≤ s , t s,t s,t < < < n n n),表示罐笼次数上限、起点工位和终点工位。
输出描述
输出一个整数:最短耗时。若 s = t s=t s=t,输出 0。若无法到达,输出 -1。答案可能很大,请使用 64 位整数。
样例1
输入
5 4
0 1 7
1 2 7
2 3 7
3 4 7
2
0 2
2 4
2 0 4
输出
14
说明
先坐罐笼从 0 到 2(耗时 0),此时不能立刻再坐 2 到 4 的罐笼。沿巷道 2 → \to → 3 → \to → 4 耗时 7 + + + 7 = = = 14。全程只走巷道则要 28。
样例2
输入
4 1
0 1 9
0
0 2 2
输出
0
说明
起点和终点都是工位 2,不必移动。
样例3
输入
3 1
0 1 8
0
2 0 2
输出
-1
说明
工位 2 与 0、1 都不连通,无法到达。
题解
思路
解题思路: 最短路算法
- 本题相比普通单点最短路题型,额外引入使用罐笼数量和本轮是否可以使用罐笼。所以定义状态
dist[u][k][state]表示到达u时,已经乘坐k次罐笼,上一轮使用罐笼为state- state =0: 未使用,本次可以使用罐笼。起点或者上一次操作是巷道
- state = 1:使用,当前不能乘罐笼,也就是上一次操作是罐笼
- 代码基本和普通最短路差不多,优先队列维持状态为
{工位, 距离,使用罐笼次数,本轮能否使用罐笼},代码基本逻辑可查看下面代码 - 算法平均时间复杂度为
O((n + q)(c + 1) log(n(c + 1)))
C++
cpp
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll INF = LLONG_MAX / 4;
struct Road {
int to;
ll w;
};
struct State {
ll dist;
int u;
int k;
int state;
bool operator>(const State& other) const {
return dist > other.dist;
}
};
ll solve(int n, vector<vector<Road>>& roads, vector<vector<int>>& elevators, int c, int s, int t) {
if (s == t) {
return 0;
}
// dist[u][k][state] state = 0:起点或者上一次走的是巷道,可以乘罐笼 state = 1:上一次乘的是罐笼,不能立即再次乘罐笼
vector<vector<vector<ll>>> dist(n, vector<vector<ll>>(c + 1, vector<ll>(2, INF)));
priority_queue<State, vector<State>, greater<State>> pq;
dist[s][0][0] = 0;
pq.push({0, s, 0, 0});
while (!pq.empty()) {
auto cur = pq.top();
pq.pop();
ll d = cur.dist;
int u = cur.u;
int k = cur.k;
int state = cur.state;
if (d != dist[u][k][state]) {
continue;
}
// 走巷道
for (auto& edge : roads[u]){
int v = edge.to;
ll nd = d + edge.w;
// 走过巷道后,可以再次乘罐笼
if (nd < dist[v][k][0]) {
dist[v][k][0] = nd;
pq.push({nd, v, k, 0});
}
}
// 只有没有刚刚乘坐罐笼,并且没有超过次数限制时才能乘
if (state == 0 && k < c) {
for (auto& v : elevators[u]) {
ll nd = d;
if (nd < dist[v][k + 1][1]) {
dist[v][k + 1][1] = nd;
pq.push({nd, v, k + 1, 1});
}
}
}
}
ll ans = INF;
for (int k = 0; k <= c; k++) {
ans = min(ans, dist[t][k][0]);
ans = min(ans, dist[t][k][1]);
}
return ans == INF ? -1 : ans;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n,q;
cin >> n >> q;
vector<vector<Road>> roads(n);
for (int i = 0; i < q; i++) {
int x, y;
ll d;
cin >> x >> y >>d;
roads[x].push_back({y,d});
roads[y].push_back({x,d});
}
int b;
cin >> b;
vector<vector<int>> elevators(n);
for (int i = 0; i < b; i++) {
int a, f;
cin >> a >> f;
elevators[a].push_back(f);
elevators[f].push_back(a);
}
int c,s,t;
cin >> c >> s >> t;
cout << solve(n, roads, elevators, c, s, t) << endl;
return 0;
}
java
java
import java.io.*;
import java.util.*;
public class Main {
static final long INF = Long.MAX_VALUE / 4;
static class Road {
int to;
long w;
Road(int to, long w) {
this.to = to;
this.w = w;
}
}
static class State implements Comparable<State> {
long dist;
int u;
int k;
int state;
State(long dist, int u, int k, int state) {
this.dist = dist;
this.u = u;
this.k = k;
this.state = state;
}
@Override
public int compareTo(State other) {
return Long.compare(this.dist, other.dist);
}
}
static long solve(int n, ArrayList<Road>[] roads, ArrayList<Integer>[] elevators,
int c, int s, int t) {
if (s == t) {
return 0;
}
// dist[u][k][state] state = 0:起点或者上一次走的是巷道,可以乘罐笼 state = 1:上一次乘的是罐笼,不能立即再次乘罐笼
long[][][] dist = new long[n][c + 1][2];
for (int i = 0; i < n; i++) {
for (int j = 0; j <= c; j++) {
Arrays.fill(dist[i][j], INF);
}
}
PriorityQueue<State> pq = new PriorityQueue<>();
dist[s][0][0] = 0;
pq.offer(new State(0, s, 0, 0));
while (!pq.isEmpty()) {
State cur = pq.poll();
long d = cur.dist;
int u = cur.u;
int k = cur.k;
int state = cur.state;
if (d != dist[u][k][state]) {
continue;
}
// 走巷道
for (Road edge : roads[u]) {
int v = edge.to;
long nd = d + edge.w;
// 走过巷道后,可以再次乘罐笼
if (nd < dist[v][k][0]) {
dist[v][k][0] = nd;
pq.offer(new State(nd, v, k, 0));
}
}
// 只有没有刚刚乘坐罐笼,并且没有超过次数限制时才能乘
if (state == 0 && k < c) {
for (int v : elevators[u]) {
long nd = d;
if (nd < dist[v][k + 1][1]) {
dist[v][k + 1][1] = nd;
pq.offer(new State(nd, v, k + 1, 1));
}
}
}
}
long ans = INF;
for (int k = 0; k <= c; k++) {
ans = Math.min(ans, dist[t][k][0]);
ans = Math.min(ans, dist[t][k][1]);
}
return ans == INF ? -1 : ans;
}
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
int n = Integer.parseInt(st.nextToken());
int q = Integer.parseInt(st.nextToken());
ArrayList<Road>[] roads = new ArrayList[n];
for (int i = 0; i < n; i++) {
roads[i] = new ArrayList<>();
}
for (int i = 0; i < q; i++) {
st = new StringTokenizer(br.readLine());
int x = Integer.parseInt(st.nextToken());
int y = Integer.parseInt(st.nextToken());
long d = Long.parseLong(st.nextToken());
roads[x].add(new Road(y, d));
roads[y].add(new Road(x, d));
}
int b = Integer.parseInt(br.readLine());
ArrayList<Integer>[] elevators = new ArrayList[n];
for (int i = 0; i < n; i++) {
elevators[i] = new ArrayList<>();
}
for (int i = 0; i < b; i++) {
st = new StringTokenizer(br.readLine());
int a = Integer.parseInt(st.nextToken());
int f = Integer.parseInt(st.nextToken());
elevators[a].add(f);
elevators[f].add(a);
}
st = new StringTokenizer(br.readLine());
int c = Integer.parseInt(st.nextToken());
int s = Integer.parseInt(st.nextToken());
int t = Integer.parseInt(st.nextToken());
System.out.println(solve(n, roads, elevators, c, s, t));
}
}
python
python
import sys
import heapq
INF = 10**30
class Road:
def __init__(self, to, w):
self.to = to
self.w = w
class State:
def __init__(self, dist, u, k, state):
self.dist = dist
self.u = u
self.k = k
self.state = state
def solve(n, roads, elevators, c, s, t):
if s == t:
return 0
# dist[u][k][state] state = 0:起点或者上一次走的是巷道,可以乘罐笼 state = 1:上一次乘的是罐笼,不能立即再次乘罐笼
dist = [[[INF, INF] for _ in range(c + 1)] for _ in range(n)]
pq = []
dist[s][0][0] = 0
heapq.heappush(pq, (0, s, 0, 0))
while pq:
d, u, k, state = heapq.heappop(pq)
if d != dist[u][k][state]:
continue
# 走巷道
for edge in roads[u]:
v = edge.to
nd = d + edge.w
# 走过巷道后,可以再次乘罐笼
if nd < dist[v][k][0]:
dist[v][k][0] = nd
heapq.heappush(pq, (nd, v, k, 0))
# 只有没有刚刚乘坐罐笼,并且没有超过次数限制时才能乘
if state == 0 and k < c:
for v in elevators[u]:
nd = d
if nd < dist[v][k + 1][1]:
dist[v][k + 1][1] = nd
heapq.heappush(pq, (nd, v, k + 1, 1))
ans = INF
for k in range(c + 1):
ans = min(ans, dist[t][k][0])
ans = min(ans, dist[t][k][1])
return -1 if ans == INF else ans
def main():
input = sys.stdin.readline
n, q = map(int, input().split())
roads = [[] for _ in range(n)]
for _ in range(q):
x, y, d = input().split()
x = int(x)
y = int(y)
d = int(d)
roads[x].append(Road(y, d))
roads[y].append(Road(x, d))
b = int(input())
elevators = [[] for _ in range(n)]
for _ in range(b):
a, f = map(int, input().split())
elevators[a].append(f)
elevators[f].append(a)
c, s, t = map(int, input().split())
print(solve(n, roads, elevators, c, s, t))
if __name__ == "__main__":
main()
javascript
js
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
const input = [];
rl.on('line', line => {
input.push(line.trim());
});
rl.on('close', () => {
let index = 0;
const [n, q] = input[index++].split(/\s+/).map(Number);
class Road {
constructor(to, w) {
this.to = to;
this.w = w;
}
}
class MinHeap {
constructor() {
this.heap = [];
}
push(item) {
this.heap.push(item);
let i = this.heap.length - 1;
while (i > 0) {
const p = Math.floor((i - 1) / 2);
if (this.heap[p][0] <= item[0]) {
break;
}
this.heap[i] = this.heap[p];
i = p;
}
this.heap[i] = item;
}
pop() {
const top = this.heap[0];
const last = this.heap.pop();
if (this.heap.length > 0) {
let i = 0;
while (true) {
let left = i * 2 + 1;
let right = i * 2 + 2;
if (left >= this.heap.length) {
break;
}
let child = left;
if (right < this.heap.length &&
this.heap[right][0] < this.heap[left][0]) {
child = right;
}
if (this.heap[child][0] >= last[0]) {
break;
}
this.heap[i] = this.heap[child];
i = child;
}
this.heap[i] = last;
}
return top;
}
isEmpty() {
return this.heap.length === 0;
}
}
function solve(n, roads, elevators, c, s, t) {
if (s === t) {
return 0;
}
const INF = Number.MAX_SAFE_INTEGER / 4;
// dist[u][k][state] state = 0:起点或者上一次走的是巷道,可以乘罐笼 state = 1:上一次乘的是罐笼,不能立即再次乘罐笼
const dist = Array.from(
{ length: n },
() => Array.from(
{ length: c + 1 },
() => [INF, INF]
)
);
const pq = new MinHeap();
dist[s][0][0] = 0;
pq.push([0, s, 0, 0]);
while (!pq.isEmpty()) {
const cur = pq.pop();
const d = cur[0];
const u = cur[1];
const k = cur[2];
const state = cur[3];
if (d !== dist[u][k][state]) {
continue;
}
// 走巷道
for (const edge of roads[u]) {
const v = edge.to;
const nd = d + edge.w;
// 走过巷道后,可以再次乘罐笼
if (nd < dist[v][k][0]) {
dist[v][k][0] = nd;
pq.push([nd, v, k, 0]);
}
}
// 只有没有刚刚乘坐罐笼,并且没有超过次数限制时才能乘
if (state === 0 && k < c) {
for (const v of elevators[u]) {
const nd = d;
if (nd < dist[v][k + 1][1]) {
dist[v][k + 1][1] = nd;
pq.push([nd, v, k + 1, 1]);
}
}
}
}
let ans = INF;
for (let k = 0; k <= c; k++) {
ans = Math.min(ans, dist[t][k][0]);
ans = Math.min(ans, dist[t][k][1]);
}
return ans === INF ? -1 : ans;
}
const roads = Array.from({ length: n }, () => []);
for (let i = 0; i < q; i++) {
const [x, y, d] = input[index++].split(/\s+/).map(Number);
roads[x].push(new Road(y, d));
roads[y].push(new Road(x, d));
}
const b = Number(input[index++]);
const elevators = Array.from({ length: n }, () => []);
for (let i = 0; i < b; i++) {
const [a, f] = input[index++].split(/\s+/).map(Number);
elevators[a].push(f);
elevators[f].push(a);
}
const [c, s, t] = input[index++].split(/\s+/).map(Number);
console.log(solve(n, roads, elevators, c, s, t));
});
Go
go
package main
import (
"bufio"
"container/heap"
"fmt"
"os"
)
const INF int64 = 1<<62 - 1
type Road struct {
to int
w int64
}
type State struct {
dist int64
u int
k int
state int
}
type PriorityQueue []State
func (pq PriorityQueue) Len() int {
return len(pq)
}
func (pq PriorityQueue) Less(i, j int) bool {
return pq[i].dist < pq[j].dist
}
func (pq PriorityQueue) Swap(i, j int) {
pq[i], pq[j] = pq[j], pq[i]
}
func (pq *PriorityQueue) Push(x interface{}) {
*pq = append(*pq, x.(State))
}
func (pq *PriorityQueue) Pop() interface{} {
old := *pq
n := len(old)
item := old[n-1]
*pq = old[:n-1]
return item
}
func solve(n int, roads [][]Road, elevators [][]int, c, s, t int) int64 {
if s == t {
return 0
}
// dist[u][k][state] state = 0:起点或者上一次走的是巷道,可以乘罐笼 state = 1:上一次乘的是罐笼,不能立即再次乘罐笼
dist := make([][][]int64, n)
for i := 0; i < n; i++ {
dist[i] = make([][]int64, c+1)
for j := 0; j <= c; j++ {
dist[i][j] = []int64{INF, INF}
}
}
pq := &PriorityQueue{}
heap.Init(pq)
dist[s][0][0] = 0
heap.Push(pq, State{0, s, 0, 0})
for pq.Len() > 0 {
cur := heap.Pop(pq).(State)
d := cur.dist
u := cur.u
k := cur.k
state := cur.state
if d != dist[u][k][state] {
continue
}
// 走巷道
for _, edge := range roads[u] {
v := edge.to
nd := d + edge.w
// 走过巷道后,可以再次乘罐笼
if nd < dist[v][k][0] {
dist[v][k][0] = nd
heap.Push(pq, State{nd, v, k, 0})
}
}
// 只有没有刚刚乘坐罐笼,并且没有超过次数限制时才能乘
if state == 0 && k < c {
for _, v := range elevators[u] {
nd := d
if nd < dist[v][k+1][1] {
dist[v][k+1][1] = nd
heap.Push(pq, State{nd, v, k + 1, 1})
}
}
}
}
var ans int64 = INF
for k := 0; k <= c; k++ {
if dist[t][k][0] < ans {
ans = dist[t][k][0]
}
if dist[t][k][1] < ans {
ans = dist[t][k][1]
}
}
if ans == INF {
return -1
}
return ans
}
func main() {
in := bufio.NewReader(os.Stdin)
out := bufio.NewWriter(os.Stdout)
defer out.Flush()
var n, q int
fmt.Fscan(in, &n, &q)
roads := make([][]Road, n)
for i := 0; i < q; i++ {
var x, y int
var d int64
fmt.Fscan(in, &x, &y, &d)
roads[x] = append(roads[x], Road{y, d})
roads[y] = append(roads[y], Road{x, d})
}
var b int
fmt.Fscan(in, &b)
elevators := make([][]int, n)
for i := 0; i < b; i++ {
var a, f int
fmt.Fscan(in, &a, &f)
elevators[a] = append(elevators[a], f)
elevators[f] = append(elevators[f], a)
}
var c, s, t int
fmt.Fscan(in, &c, &s, &t)
fmt.Fprintln(out, solve(n, roads, elevators, c, s, t))
}