拼多多笔试真题 9.6 - 限次罐笼(C++/Py/Java /Js/Go)

限次罐笼

拼多多 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 104
  • 0 ≤ \le ≤ q q q ≤ \le ≤ 2 × 10 5 2 \times 10^5 2×105
  • 0 ≤ \le ≤ b b b ≤ \le ≤ 10
  • 0 ≤ \le ≤ c c c ≤ \le ≤ 10
  • 0 ≤ \le ≤ d i d_i di ≤ \le ≤ 1000000000
  • 0 ≤ \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 ≤ 100 ≤ \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

说明

先坐罐笼从 02(耗时 0),此时不能立刻再坐 24 的罐笼。沿巷道 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

说明

工位 201 都不连通,无法到达。

题解

思路

解题思路: 最短路算法

  1. 本题相比普通单点最短路题型,额外引入使用罐笼数量和本轮是否可以使用罐笼。所以定义状态dist[u][k][state]表示到达u时,已经乘坐k次罐笼,上一轮使用罐笼为state
    • state =0: 未使用,本次可以使用罐笼。起点或者上一次操作是巷道
    • state = 1:使用,当前不能乘罐笼,也就是上一次操作是罐笼
  2. 代码基本和普通最短路差不多,优先队列维持状态为{工位, 距离,使用罐笼次数,本轮能否使用罐笼},代码基本逻辑可查看下面代码
  3. 算法平均时间复杂度为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))
}
相关推荐
无限码力12 天前
拼多多笔试真题 2026-8-30【唯一入栈出库序列】
算法·pdd笔试真题·拼多多春招笔试真题·拼多多秋招笔试真题·拼多多笔试真题题解·拼多多最新笔试题库
无限码力12 天前
2026-8-30拼多多笔试真题-哪个查阅窗能留空(C++/Py/Java /Js/Go)
算法·pdd·pdd笔试真题·拼多多春招笔试真题·拼多多秋招笔试真题·拼多多笔试真题题解·拼多多笔试题解
无限码力15 天前
pdd笔试真题-和差方程最少标定(C++/Py/Java /Js/Go)
pdd·拼多多笔试真题·pdd笔试真题·拼多多技术岗笔试真题·拼多多秋招笔试·拼多多笔试题解
无限码力15 天前
拼多多 春招&秋招 笔试真题题库目录|笔试题库 + 算法考点详解
算法·pdd笔试真题·pdd笔试真题题解·拼多多春招笔试真题·拼多多秋招笔试真题·拼多多笔试真题题解
无限码力16 天前
pdd笔试真题-驿站补给最短耗时(C++/Py/Java /Js/Go)
pdd·拼多多笔试真题·拼多多机试·拼多多技术岗笔试·pdd笔试真题·拼多多技术岗笔试真题·pdd笔试真题题解
无限码力1 个月前
拼多多笔试真题-评价展示序列(C++/Py/Java /Js/Go)
拼多多·pdd·拼多多机试·拼多多技术岗笔试·拼多多笔试·拼多多技术岗笔试真题
无限码力1 个月前
拼多多笔试真题-多多送快递(C++/Py/Java /Js/Go)
拼多多·pdd·拼多多笔试真题·拼多多机试·拼多多笔试·拼多多技术岗真题
无限码力1 个月前
拼多多笔试真题-多多的告警网络(C++/Py/Java /Js/Go)
拼多多·pdd·拼多多笔试真题·拼多多笔试·拼多多技术岗笔试真题
无限码力1 个月前
拼多多笔试真题-多多的GPU批处理调度(C++/Py/Java /Js/Go)
拼多多·pdd·拼多多笔试真题·拼多多机试·拼多多笔试·拼多多技术岗笔试真题