驿站补给最短耗时
拼多多 8月23号笔试 真题 第三题
题目内容
巡检车要从 111 号驿站赶到 CCC 号驿站。沿线共有 CCC 个驿站、EEE 条双向土路。第 iii 条路连接 xix_ixi 与 yiy_iyi,走过要消耗 aia_iai 格能量、耗时 wiw_iwi。
车载电池容量为 BBB,出发时是满的,电量不能超过 BBB。在第 jjj 号驿站可以一格一格充电,每充 1 格耗时 sjs_jsj。请计算从 111 赶到 CCC 的最短时间;若怎么走都到不了,输出 -1。
输入描述
第一行三个整数 CCC、EEE、BBB,表示驿站数、土路数与电池容量(2≤C≤1032\le C\le 10^32≤C≤103,1≤E≤1041\le E\le 10^41≤E≤104,1≤B≤1021\le B\le 10^21≤B≤102)。
第二行 CCC 个整数 s1,s2,...,sCs_1,s_2,\ldots,s_Cs1,s2,...,sC(0≤sj≤1020\le s_j\le 10^20≤sj≤102),表示各驿站充 1 格的耗时。
接下来 EEE 行,每行四个整数 xi,yi,ai,wix_i,y_i,a_i,w_ixi,yi,ai,wi(1≤xi,yi≤C1\le x_i,y_i\le C1≤xi,yi≤C,1≤ai≤1021\le a_i\le 10^21≤ai≤102,1≤wi≤1031\le w_i\le 10^31≤wi≤103),描述一条双向土路。
输出描述
输出一个整数:赶到 CCC 号驿站的最短时间。无法到达时输出 -1。
样例1
输入
4 3 5
2 1 9 3
1 2 3 4
2 3 3 5
3 4 2 6
输出
18
说明
出发电量为 5。先走 1→21\to 21→2(耗时 4,剩 2),在 222 号驿站充 3 格(耗时 3),再走 2→3→42\to 3\to 42→3→4(耗时 5+6)。总时间 18。直接在 333 号驿站用单价 9 充电会更慢。
样例2
输入
2 1 3
1 1
1 2 4 10
输出
-1
说明
唯一一条路要消耗 4 格,超过容量 3,无法通行。
样例3
输入
3 2 4
0 5 1
1 2 2 3
2 3 2 4
输出
7
说明
111 号驿站充电耗时为 0,但出发已经满电,沿 1→2→31\to 2\to 31→2→3 耗时 3+4=7,不必再充。
数据范围
- 2≤C≤1032\le C\le 10^32≤C≤103
- 1≤E≤1041\le E\le 10^41≤E≤104
- 1≤B≤1021\le B\le 10^21≤B≤102
- 0≤sj≤1020\le s_j\le 10^20≤sj≤102
- 1≤ai≤1021\le a_i\le 10^21≤ai≤102
- 1≤wi≤1031\le w_i\le 10^31≤wi≤103
- 所有输入均为整数
题解
思路
解题思路: 最短路算法
- 本题相比普通单点最短路题型,额外引入了电量限制,加上电量
c <= 100可额外增加一个状态。dist[u][c]表示到达u电量为c的最短时间。 - 代码基本和普通最短路差不多,额外加入一步,刚到达u并且电量小于B时,可以充电一次,然后重新入队即可。
- 算法平均时间复杂度为
O((CB + EB) log(CB))
C++
cpp
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
struct Edge {
int to;
int cost;
int time;
};
struct Node {
ll dist;
int u;
int battery;
bool operator>(const Node& other) const {
return dist > other.dist;
}
};
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int C, E, B;
cin >> C >> E >> B;
vector<int> s(C + 1);
for (int i = 1 ; i <= C; i++) {
cin >> s[i];
}
vector<vector<Edge>> graph(C + 1);
for (int i = 0; i < E; i++) {
int x, y, a, w;
cin >> x >> y >> a >> w;
// 电池容量不足以通过的路,可以直接忽略
if (a <= B) {
graph[x].push_back({y, a, w});
graph[y].push_back({x, a, w});
}
}
const ll INF = LLONG_MAX / 4;
// dist[u][b]:到达 u,剩余 b 格电量时的最短时间
vector<vector<ll>> dist(C + 1, vector<ll>(B + 1, INF));
priority_queue<Node, vector<Node>, greater<Node>> pq;
// 1 号驿站出发时电量为 B
dist[1][B] = 0;
pq.push({0, 1, B});
while (!pq.empty()) {
auto [d, u, battery] = pq.top();
pq.pop();
if (d != dist[u][battery]) {
continue;
}
// 在当前驿站充 1 格电
if (battery < B) {
ll nd = d + s[u];
// 入队列
if (nd < dist[u][battery + 1]) {
dist[u][battery + 1] = nd;
pq.push({nd, u, battery + 1});
}
}
// 尝试通行
for (auto &e : graph[u]) {
if (battery < e.cost) {
continue;
}
int nextBattery = battery - e.cost;
ll nd = d + e.time;
if (nd < dist[e.to][nextBattery]) {
dist[e.to][nextBattery] = nd;
pq.push({nd, e.to, nextBattery});
}
}
}
ll ans = INF;
for (int battery = 0; battery <= B; battery++) {
ans = min(ans, dist[C][battery]);
}
if (ans == INF) {
cout << -1 << '\n';
} else {
cout << ans << '\n';
}
return 0;
}
java
java
import java.io.*;
import java.util.*;
public class Main {
static class Edge {
int to;
int cost;
int time;
Edge(int to, int cost, int time) {
this.to = to;
this.cost = cost;
this.time = time;
}
}
static class Node implements Comparable<Node> {
long dist;
int u;
int battery;
Node(long dist, int u, int battery) {
this.dist = dist;
this.u = u;
this.battery = battery;
}
@Override
public int compareTo(Node other) {
return Long.compare(dist, other.dist);
}
}
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
int C = Integer.parseInt(st.nextToken());
int E = Integer.parseInt(st.nextToken());
int B = Integer.parseInt(st.nextToken());
int[] s = new int[C + 1];
st = new StringTokenizer(br.readLine());
for (int i = 1; i <= C; i++) {
s[i] = Integer.parseInt(st.nextToken());
}
List<Edge>[] graph = new ArrayList[C + 1];
for (int i = 1; i <= C; i++) {
graph[i] = new ArrayList<>();
}
for (int i = 0; i < E; i++) {
st = new StringTokenizer(br.readLine());
int x = Integer.parseInt(st.nextToken());
int y = Integer.parseInt(st.nextToken());
int a = Integer.parseInt(st.nextToken());
int w = Integer.parseInt(st.nextToken());
// 电池容量不足以通过的路,可以直接忽略
if (a <= B) {
graph[x].add(new Edge(y, a, w));
graph[y].add(new Edge(x, a, w));
}
}
final long INF = Long.MAX_VALUE / 4;
// dist[u][b]:到达 u,剩余 b 格电量时的最短时间
long[][] dist = new long[C + 1][B + 1];
for (int i = 1; i <= C; i++) {
Arrays.fill(dist[i], INF);
}
PriorityQueue<Node> pq = new PriorityQueue<>();
// 1 号驿站出发时电量为 B
dist[1][B] = 0;
pq.offer(new Node(0, 1, B));
while (!pq.isEmpty()) {
Node cur = pq.poll();
long d = cur.dist;
int u = cur.u;
int battery = cur.battery;
if (d != dist[u][battery]) {
continue;
}
// 在当前驿站充 1 格电
if (battery < B) {
long nd = d + s[u];
// 入队列
if (nd < dist[u][battery + 1]) {
dist[u][battery + 1] = nd;
pq.offer(new Node(nd, u, battery + 1));
}
}
// 尝试通行
for (Edge e : graph[u]) {
if (battery < e.cost) {
continue;
}
int nextBattery = battery - e.cost;
long nd = d + e.time;
if (nd < dist[e.to][nextBattery]) {
dist[e.to][nextBattery] = nd;
pq.offer(new Node(nd, e.to, nextBattery));
}
}
}
long ans = INF;
for (int battery = 0; battery <= B; battery++) {
ans = Math.min(ans, dist[C][battery]);
}
if (ans == INF) {
System.out.println(-1);
} else {
System.out.println(ans);
}
}
}
python
python
import sys
import heapq
# 差分 + 贪心判断
# 这里实际使用的是 Dijkstra + 电量状态
data = list(map(int, sys.stdin.buffer.read().split()))
idx = 0
C = data[idx]
E = data[idx + 1]
B = data[idx + 2]
idx += 3
s = [0] * (C + 1)
for i in range(1, C + 1):
s[i] = data[idx]
idx += 1
graph = [[] for _ in range(C + 1)]
for _ in range(E):
x = data[idx]
y = data[idx + 1]
a = data[idx + 2]
w = data[idx + 3]
idx += 4
# 电池容量不足以通过的路,可以直接忽略
if a <= B:
graph[x].append((y, a, w))
graph[y].append((x, a, w))
INF = float('inf')
# dist[u][b]:到达 u,剩余 b 格电量时的最短时间
dist = [[INF] * (B + 1) for _ in range(C + 1)]
pq = []
# 1 号驿站出发时电量为 B
dist[1][B] = 0
heapq.heappush(pq, (0, 1, B))
while pq:
d, u, battery = heapq.heappop(pq)
if d != dist[u][battery]:
continue
# 在当前驿站充 1 格电
if battery < B:
nd = d + s[u]
# 入队列
if nd < dist[u][battery + 1]:
dist[u][battery + 1] = nd
heapq.heappush(pq, (nd, u, battery + 1))
# 尝试通行
for v, cost, time in graph[u]:
if battery < cost:
continue
next_battery = battery - cost
nd = d + time
if nd < dist[v][next_battery]:
dist[v][next_battery] = nd
heapq.heappush(pq, (nd, v, next_battery))
ans = INF
for battery in range(B + 1):
ans = min(ans, dist[C][battery])
if ans == INF:
print(-1)
else:
print(ans)
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().split(/\s+/));
});
rl.on('close', () => {
let idx = 0;
const C = Number(input[idx++]);
const E = Number(input[idx++]);
const B = Number(input[idx++]);
const s = new Array(C + 1).fill(0);
for (let i = 1; i <= C; i++) {
s[i] = Number(input[idx++]);
}
const graph = Array.from({ length: C + 1 }, () => []);
for (let i = 0; i < E; i++) {
const x = Number(input[idx++]);
const y = Number(input[idx++]);
const a = Number(input[idx++]);
const w = Number(input[idx++]);
// 电池容量不足以通过的路,可以直接忽略
if (a <= B) {
graph[x].push([y, a, w]);
graph[y].push([x, a, w]);
}
}
const INF = Number.MAX_SAFE_INTEGER;
// dist[u][b]:到达 u,剩余 b 格电量时的最短时间
const dist = Array.from(
{ length: C + 1 },
() => new Array(B + 1).fill(INF)
);
// 简单二叉堆优先队列
const pq = [];
function push(node) {
pq.push(node);
let i = pq.length - 1;
while (i > 0) {
const parent = Math.floor((i - 1) / 2);
if (pq[parent][0] <= pq[i][0]) {
break;
}
[pq[parent], pq[i]] = [pq[i], pq[parent]];
i = parent;
}
}
function pop() {
const result = pq[0];
const last = pq.pop();
if (pq.length > 0) {
pq[0] = last;
let i = 0;
while (true) {
let smallest = i;
const left = i * 2 + 1;
const right = i * 2 + 2;
if (left < pq.length && pq[left][0] < pq[smallest][0]) {
smallest = left;
}
if (right < pq.length && pq[right][0] < pq[smallest][0]) {
smallest = right;
}
if (smallest === i) {
break;
}
[pq[i], pq[smallest]] = [pq[smallest], pq[i]];
i = smallest;
}
}
return result;
}
// 1 号驿站出发时电量为 B
dist[1][B] = 0;
push([0, 1, B]);
while (pq.length > 0) {
const [d, u, battery] = pop();
if (d !== dist[u][battery]) {
continue;
}
// 在当前驿站充 1 格电
if (battery < B) {
const nd = d + s[u];
// 入队列
if (nd < dist[u][battery + 1]) {
dist[u][battery + 1] = nd;
push([nd, u, battery + 1]);
}
}
// 尝试通行
for (const edge of graph[u]) {
const [v, cost, time] = edge;
if (battery < cost) {
continue;
}
const nextBattery = battery - cost;
const nd = d + time;
if (nd < dist[v][nextBattery]) {
dist[v][nextBattery] = nd;
push([nd, v, nextBattery]);
}
}
}
let ans = INF;
for (let battery = 0; battery <= B; battery++) {
ans = Math.min(ans, dist[C][battery]);
}
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
cost int
time int
}
type Node struct {
dist int64
u int
battery int
}
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)
x := old[n-1]
*pq = old[:n-1]
return x
}
func main() {
in := bufio.NewReader(os.Stdin)
out := bufio.NewWriter(os.Stdout)
defer out.Flush()
var C, E, B int
fmt.Fscan(in, &C, &E, &B)
s := make([]int, C+1)
for i := 1; i <= C; i++ {
fmt.Fscan(in, &s[i])
}
graph := make([][]Edge, C+1)
for i := 0; i < E; i++ {
var x, y, a, w int
fmt.Fscan(in, &x, &y, &a, &w)
// 电池容量不足以通过的路,可以直接忽略
if a <= B {
graph[x] = append(graph[x], Edge{y, a, w})
graph[y] = append(graph[y], Edge{x, a, w})
}
}
const INF int64 = 1 << 62
// dist[u][b]:到达 u,剩余 b 格电量时的最短时间
dist := make([][]int64, C+1)
for i := 1; i <= C; i++ {
dist[i] = make([]int64, B+1)
for j := 0; j <= B; j++ {
dist[i][j] = INF
}
}
pq := &PriorityQueue{}
heap.Init(pq)
// 1 号驿站出发时电量为 B
dist[1][B] = 0
heap.Push(pq, Node{0, 1, B})
for pq.Len() > 0 {
cur := heap.Pop(pq).(Node)
d := cur.dist
u := cur.u
battery := cur.battery
if d != dist[u][battery] {
continue
}
// 在当前驿站充 1 格电
if battery < B {
nd := d + int64(s[u])
// 入队列
if nd < dist[u][battery+1] {
dist[u][battery+1] = nd
heap.Push(pq, Node{nd, u, battery + 1})
}
}
// 尝试通行
for _, e := range graph[u] {
if battery < e.cost {
continue
}
nextBattery := battery - e.cost
nd := d + int64(e.time)
if nd < dist[e.to][nextBattery] {
dist[e.to][nextBattery] = nd
heap.Push(pq, Node{nd, e.to, nextBattery})
}
}
}
var ans int64 = INF
for battery := 0; battery <= B; battery++ {
if dist[C][battery] < ans {
ans = dist[C][battery]
}
}
if ans == INF {
fmt.Fprintln(out, -1)
} else {
fmt.Fprintln(out, ans)
}
}