拼多多笔试真题-多多送快递(C++/Py/Java /Js/Go)

多多送快递

拼多多技术岗 8月2号笔试 第三题

题目内容

多多在一家电商平台做物流调度。平台在 NNN 个城市之间建立了 MMM 条有向运输线路,每条线路从城市 uuu 到城市 vvv,需要支付邮费 www 元。

一位顾客在城市 111 下单,商品需要从城市 111 运送到城市 NNN。多多手里恰好有一张免邮券,可以免除任意一条两个城市间有向运输线路的邮费(将该条线路的邮费变为 000)。

请帮助多多计算从城市 111 到城市 NNN 的最小总邮费。如果即使使用免邮券也无法到达城市 NNN,输出 −1-1−1。

输入描述

第一行两个整数 N,MN, MN,M,分别表示城市数量和运输线路数量。

其中 NNN 表示共有 NNN 个城市,MMM 表示共有 MMM 条有向运输线路。 (2≤N≤100000, 0≤M≤200000)(2 \le N \le 100000,\ 0 \le M \le 200000)(2≤N≤100000, 0≤M≤200000)

接下来 MMM 行,每行三个整数 u,v,wu, v, wu,v,w,表示一条从城市 uuu 到城市 vvv 的有向运输线路,其中 www 表示通过该线路需要支付的邮费。 (1≤u,v≤N, 1≤w≤10000)(1 \le u, v \le N,\ 1 \le w \le 10000)(1≤u,v≤N, 1≤w≤10000)

输出描述

输出一个整数,表示从城市 111 到城市 NNN 的最小总邮费。如果无法到达,输出 −1-1−1。

样例1

输入

复制代码
4 4
1 2 2
1 3 5
2 4 3
3 4 1

输出

复制代码
1

说明

  • 不使用免邮券:最短路径 1→2→4=2+3=51 \to 2 \to 4 = 2+3=51→2→4=2+3=5,或 1→3→4=5+1=61 \to 3 \to 4 = 5+1=61→3→4=5+1=6,最小为 555
  • 免邮 1→31 \to 31→3(邮费 5→05 \to 05→0):走 1→3→4=0+1=11 \to 3 \to 4 = 0+1=11→3→4=0+1=1
  • 免邮 1→21 \to 21→2(邮费 2→02 \to 02→0):走 1→2→4=0+3=31 \to 2 \to 4 = 0+3=31→2→4=0+3=3
  • 免邮 2→42 \to 42→4(邮费 3→03 \to 03→0):走 1→2→4=2+0=21 \to 2 \to 4 = 2+0=21→2→4=2+0=2
  • 免邮 3→43 \to 43→4(邮费 1→01 \to 01→0):走 1→3→4=5+0=51 \to 3 \to 4 = 5+0=51→3→4=5+0=5
    最优方案:免邮 1→31 \to 31→3,总邮费 111。

题解

思路

解题思路: 最短路算法

  1. 根据是否使用过免邮,拆分状态,创建虚拟节点
    • 1 - N的节点,表示到达该城市,还没有使用免邮卷
    • N + 1 - 2n虚拟节点,已经使用过免邮卷
  2. 按照状态拆分之后,遍历原边u ->v w构造三种情况边
    • 到达u之前未使用免邮,到达v不使用免邮卷u -> v w
    • 到达u之前未使用免邮,到达v使用免邮卷u -> v+N 0
    • 到达u之前使用过免邮,到达v使用免邮卷u + N -> v+N 0
  3. 上述处理之后添加虚拟边 + 虚拟节点之后就是处理最短路Dijkstra的模板题了。
  4. 最终答案为dist[N]不使用免邮 和 dist[2 * N] 使用免邮的最小值。
  5. 代码总体时间复杂度为O((N + M)logN)

C++

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
using ll = long long;

struct Node {
    ll u; // T - X
    ll v; // T + X
};

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int N;
    cin >> N;
    
    vector<Node> a(N);
    
    
    // Ti - Tj > |Xi - Xj| => Ti - Xi > Tj - Xj and Ti + Xi > Tj + Xj
    for (int i = 0; i < N; i++) {
        ll T,X;
        cin >> T >> X;
        a[i].u = T - X;
        a[i].v = T + X;
    }
    
    sort(a.begin(), a.end(), [](const Node& a, const Node& b) {
      if (a.u != b.u) {
          return a.u < b.u;
      } 
      return a.v > b.v;
    });
    
    
    // 求v的严格递增子序列长度 lis[i]代表长度为i的可取最小值
    vector<ll> lis;
    for (auto &p : a) {
        ll x = p.v;
        auto it = lower_bound(lis.begin(), lis.end(), x);
        if (it == lis.end()) {
            lis.push_back(x);
        } else {
            *it = x;
        }
    }
    cout << lis.size() << endl;
    return 0;
}

java

java 复制代码
import java.io.*;
import java.util.*;

public class Main {

    static class Edge {
        int to;
        int w;

        Edge(int to, int w) {
            this.to = to;
            this.w = w;
        }
    }


    static class Node implements Comparable<Node> {
        int id;
        long dist;

        Node(int id, long dist) {
            this.id = id;
            this.dist = dist;
        }

        // 小根堆
        @Override
        public int compareTo(Node other) {
            return Long.compare(this.dist, other.dist);
        }
    }


    public static void main(String[] args) throws Exception {

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        String[] first = br.readLine().split(" ");
        int N = Integer.parseInt(first[0]);
        int M = Integer.parseInt(first[1]);

        // 分层图
        // 1 ~ N       : 未使用免邮券
        // N+1 ~ 2N    : 已经使用免邮券
        ArrayList<Edge>[] graph = new ArrayList[2 * N + 1];

        for (int i = 1; i <= 2 * N; i++) {
            graph[i] = new ArrayList<>();
        }


        for (int i = 0; i < M; i++) {

            String[] line = br.readLine().split(" ");

            int u = Integer.parseInt(line[0]);
            int v = Integer.parseInt(line[1]);
            int w = Integer.parseInt(line[2]);


            // 不使用免邮
            graph[u].add(new Edge(v, w));

            // 使用免邮 跳到v的虚拟节点
            graph[u].add(new Edge(v + N, 0));


            // 使用免邮之后 只能跳到v的虚拟节点
            graph[u + N].add(new Edge(v + N, w));
        }


        long INF = Long.MAX_VALUE / 4;

        long[] dist = new long[2 * N + 1];

        Arrays.fill(dist, INF);


        PriorityQueue<Node> pq = new PriorityQueue<>();

        dist[1] = 0;

        pq.offer(new Node(1, 0));


        while (!pq.isEmpty()) {

            Node cur = pq.poll();

            int u = cur.id;


            // 过期
            if (cur.dist != dist[u]) {
                continue;
            }


            for (Edge e : graph[u]) {

                int v = e.to;

                long nd = cur.dist + e.w;


                if (dist[v] > nd) {

                    dist[v] = nd;

                    pq.offer(new Node(v, nd));
                }
            }
        }


        // 取使用免邮不免邮的较小值
        long ans = Math.min(dist[N], dist[2 * N]);


        if (ans == INF) {
            System.out.println(-1);
        } else {
            System.out.println(ans);
        }
    }
}

python

python 复制代码
import sys
import heapq


class Edge:
    def __init__(self, to, w):
        self.to = to
        self.w = w


input = sys.stdin.readline


N, M = map(int, input().split())


# 分层图
# 1 ~ N       : 未使用免邮券
# N+1 ~ 2N    : 已经使用免邮券
graph = [[] for _ in range(2 * N + 1)]


for _ in range(M):

    u, v, w = map(int, input().split())


    # 不使用免邮
    graph[u].append(Edge(v, w))


    # 使用免邮 跳到v的虚拟节点
    graph[u].append(Edge(v + N, 0))


    # 使用免邮之后 只能跳到v的虚拟节点
    graph[u + N].append(Edge(v + N, w))


INF = 4 * 10 ** 18


dist = [INF] * (2 * N + 1)


# 小根堆
pq = []


dist[1] = 0

heapq.heappush(pq, (0, 1))


while pq:

    curDist, u = heapq.heappop(pq)


    # 过期
    if curDist != dist[u]:
        continue


    for e in graph[u]:

        v = e.to

        nd = curDist + e.w


        if dist[v] > nd:

            dist[v] = nd

            heapq.heappush(pq, (nd, v))



# 取使用免邮不免邮的较小值
ans = min(dist[N], dist[2 * N])


if ans == INF:
    print(-1)
else:
    print(ans)

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", () => {


    let idx = 0;


    let [N, M] = input[idx++].split(" ").map(Number);



    // 分层图
    // 1 ~ N       : 未使用免邮券
    // N+1 ~ 2N    : 已经使用免邮券

    let graph = Array.from(
        { length: 2 * N + 1 },
        () => []
    );


    for (let i = 0; i < M; i++) {


        let [u, v, w] = input[idx++].split(" ").map(Number);



        // 不使用免邮
        graph[u].push([v, w]);


        // 使用免邮 跳到v的虚拟节点
        graph[u].push([v + N, 0]);


        // 使用免邮之后 只能跳到v的虚拟节点
        graph[u + N].push([v + N, w]);

    }



    let INF = 4e18;


    let dist = Array(2 * N + 1).fill(INF);


    // 小根堆
    // javascript 没有内置堆,手写一个

    class MinHeap {

        constructor() {
            this.heap = [];
        }


        push(node) {

            this.heap.push(node);

            let i = this.heap.length - 1;


            while (i > 0) {

                let p = Math.floor((i - 1) / 2);

                if (this.heap[p][0] <= this.heap[i][0])
                    break;


                [this.heap[p], this.heap[i]] =
                    [this.heap[i], this.heap[p]];

                i = p;
            }
        }


        pop() {

            if (this.heap.length === 1)
                return this.heap.pop();


            let res = this.heap[0];

            this.heap[0] = this.heap.pop();


            let i = 0;


            while (true) {

                let left = i * 2 + 1;
                let right = i * 2 + 2;
                let smallest = i;


                if (
                    left < this.heap.length &&
                    this.heap[left][0] < this.heap[smallest][0]
                ) {
                    smallest = left;
                }


                if (
                    right < this.heap.length &&
                    this.heap[right][0] < this.heap[smallest][0]
                ) {
                    smallest = right;
                }


                if (smallest === i)
                    break;


                [this.heap[i], this.heap[smallest]] =
                    [this.heap[smallest], this.heap[i]];


                i = smallest;
            }


            return res;
        }


        isEmpty() {
            return this.heap.length === 0;
        }
    }



    let pq = new MinHeap();


    dist[1] = 0;

    pq.push([0, 1]);



    while (!pq.isEmpty()) {


        let [curDist, u] = pq.pop();



        // 过期
        if (curDist !== dist[u])
            continue;



        for (let e of graph[u]) {


            let v = e[0];

            let w = e[1];


            let nd = curDist + w;


            if (dist[v] > nd) {


                dist[v] = nd;

                pq.push([nd, v]);
            }
        }
    }



    // 取使用免邮不免邮的较小值
    let ans = Math.min(dist[N], dist[2 * N]);


    if (ans === INF) {

        console.log(-1);

    } else {

        console.log(ans);

    }

});

Go

go 复制代码
package main

import (
	"bufio"
	"container/heap"
	"fmt"
	"os"
)


type Edge struct {
	to int
	w  int
}


type Node struct {
	id   int
	dist int64
}


// 小根堆
type PriorityQueue []Node


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.(Node))
}


func (pq *PriorityQueue) Pop() interface{} {

	old := *pq

	n := len(old)

	item := old[n-1]

	*pq = old[:n-1]

	return item
}



func main() {


	in := bufio.NewReader(os.Stdin)


	var N, M int

	fmt.Fscan(in, &N, &M)



	// 分层图
	// 1 ~ N       : 未使用免邮券
	// N+1 ~ 2N    : 已经使用免邮券

	graph := make([][]Edge, 2*N+1)



	for i := 0; i < M; i++ {


		var u, v, w int

		fmt.Fscan(in, &u, &v, &w)



		// 不使用免邮
		graph[u] = append(graph[u], Edge{v, w})


		// 使用免邮 跳到v的虚拟节点
		graph[u] = append(graph[u], Edge{v + N, 0})


		// 使用免邮之后 只能跳到v的虚拟节点
		graph[u+N] = append(graph[u+N], Edge{v + N, w})

	}



	const INF int64 = 4e18


	dist := make([]int64, 2*N+1)


	for i := range dist {
		dist[i] = INF
	}



	pq := &PriorityQueue{}

	heap.Init(pq)



	dist[1] = 0

	heap.Push(pq, Node{1, 0})



	for pq.Len() > 0 {


		cur := heap.Pop(pq).(Node)


		u := cur.id



		// 过期
		if cur.dist != dist[u] {
			continue
		}



		for _, e := range graph[u] {


			v := e.to


			nd := cur.dist + int64(e.w)



			if dist[v] > nd {


				dist[v] = nd


				heap.Push(pq, Node{v, nd})
			}
		}
	}



	// 取使用免邮不免邮的较小值
	ans := dist[N]

	if dist[2*N] < ans {
		ans = dist[2*N]
	}



	if ans == INF {
		fmt.Println(-1)
	} else {
		fmt.Println(ans)
	}
}
相关推荐
无限码力1 天前
拼多多笔试真题-多多的告警网络(C++/Py/Java /Js/Go)
拼多多·pdd·拼多多笔试真题·拼多多笔试·拼多多技术岗笔试真题
无限码力4 天前
拼多多笔试真题-多多的GPU批处理调度(C++/Py/Java /Js/Go)
拼多多·pdd·拼多多笔试真题·拼多多机试·拼多多笔试·拼多多技术岗笔试真题
无限码力7 天前
拼多多笔试真题-多多的灰度发布(C++/Py/Java /Js/Go)
拼多多·拼多多机试·拼多多技术岗笔试·拼多多笔试·拼多多技术岗笔试真题
市象23 天前
新拼姆会是大号版“SHEIN”吗?
拼多多
无限码力1 个月前
拼多多笔试真题【多多的特殊三元组】
拼多多·拼多多笔试真题·拼多多笔试题库·拼多多技术岗笔试·pdd笔试笔试真题
无限码力1 个月前
拼多多笔试真题-多多的Boss挑战(C++/Py/Java /Js/Go)
拼多多·拼多多笔试真题·拼多多技术岗笔试题目·拼多多机试·pdd笔试真题
无限码力1 个月前
拼多多笔试真题-多多捕蝇(C++/Py/Java /Js/Go)
拼多多·拼多多笔试真题·拼多多技术岗笔试题目·拼多多机试·pdd笔试真题
无限码力1 个月前
拼多多笔试真题-多多的营救行动(C++/Py/Java /Js/Go)
拼多多·拼多多笔试真题·拼多多笔试题库·拼多多技术岗笔试
无限码力1 个月前
拼多多笔试真题-对角线遍历矩阵(C++/Py/Java /Js/Go)
矩阵·拼多多·拼多多笔试真题·拼多多技术岗笔试题目·拼多多机试