题目链接
题目描述
在宇宙中,有 个独立的虫洞网络。要进入任何一个网络,都需要消耗特定的能量。一旦进入,飞船就可以在该网络内的所有星系间无消耗地穿梭。
给定一个起点星系 和一个目标星系
,以及飞船的总备用能量
。任务是计算从
到
所需的最小总能量消耗。
- 一次旅行可能需要穿越多个虫洞网络。
- 如果无法抵达,或最低能量消耗超过
,则视为任务失败。
解题思路
这是一个最短路径问题。如果直接将星系作为图的节点,同一个虫洞网络内的所有星系都会形成一个完全连通的子图,边数会非常多,导致效率低下。
更高效的方法是构建一个包含两种不同类型节点的图:星系节点和虫洞网络节点。
1. 构建混合图模型
-
节点 (Nodes):
- 为每一个在题目中出现的星系创建一个“星系节点”。
- 为每一个虫洞网络创建一个“虫洞网络节点”。为了在同一个数组中区分它们,我们可以约定星系
g
的节点编号就是g
,而第i
个虫洞网络()的节点编号是
MAX_GALAXY_ID + i
。
-
边 (Edges): 根据旅行规则建立有向边和权重:
- 从星系到网络:对于每一个在网络
i
(成本为)中的星系
g
,我们建立一条从“星系节点g
” 到 “网络节点i
” 的有向边。这条边的权重是,代表支付能量以激活并进入该虫洞网络。
- 从网络到星系:对于同一个星系
g
和网络i
,我们再建立一条从“网络节点i
” 到 “星系节点g
” 的反向边。这条边的权重是,代表在已激活的网络内移动是无消耗的。
- 从星系到网络:对于每一个在网络
2. 应用 Dijkstra 算法
构建好这个混合图后,问题就转化为了一个标准的单源最短路径问题。
- 起点:起始星系
对应的“星系节点”。
- 算法:我们从起点
开始,运行 Dijkstra 算法,计算到图中所有其他节点的最短路径(最小能量消耗)。
- 状态:算法中的
dist[v]
数组记录了从起点到达节点
v
(无论是星系节点还是网络节点)所需的最小总能量。
3. 确定最终结果
- Dijkstra 算法结束后,
dist[t]
的值就是从星系到达星系
的最小能量消耗。
- 最后,将这个结果与飞船的总备用能量
进行比较:
- 如果
dist[t]
是一个有效值(不是无穷大)且dist[t] <= e
,则输出dist[t]
。 - 否则,说明无法抵达或能量不足,输出 -1。
- 如果
这个模型巧妙地将网络内的“任意穿梭”特性转化为了“网络节点 -> 星系节点”的 0 权重边,从而可以用标准的最短路算法高效求解。
代码
#include <iostream>
#include <vector>
#include <queue>
#include <map>
using namespace std;
using ll = long long;
using P = pair<ll, int>;
const int MAX_GALAXY_ID = 50000;
const ll INF = 1e18;
int main() {
ios::sync_with_stdio(false);
cin.tie(NULL);
int n, s, t;
ll e;
cin >> n >> s >> t >> e;
int max_node_id = MAX_GALAXY_ID + n;
vector<vector<P>> adj(max_node_id + 1);
for (int i = 0; i < n; ++i) {
ll cost;
int k;
cin >> cost >> k;
int network_node_id = MAX_GALAXY_ID + i + 1;
for (int j = 0; j < k; ++j) {
int galaxy_id;
cin >> galaxy_id;
// 从星系 -> 网络,成本为 cost
adj[galaxy_id].push_back({cost, network_node_id});
// 从网络 -> 星系,成本为 0
adj[network_node_id].push_back({0, galaxy_id});
}
}
vector<ll> dist(max_node_id + 1, INF);
priority_queue<P, vector<P>, greater<P>> pq;
dist[s] = 0;
pq.push({0, s});
while (!pq.empty()) {
auto [d, u] = pq.top();
pq.pop();
if (d > dist[u]) {
continue;
}
for (auto& edge : adj[u]) {
int v = edge.second;
ll weight = edge.first;
if (dist[u] + weight < dist[v]) {
dist[v] = dist[u] + weight;
pq.push({dist[v], v});
}
}
}
if (dist[t] == INF || dist[t] > e) {
cout << -1 << endl;
} else {
cout << dist[t] << endl;
}
return 0;
}
import java.util.*;
public class Main {
static final int MAX_GALAXY_ID = 50000;
static final long INF = (long) 1e18;
static class Edge {
int to;
long cost;
Edge(int to, long cost) {
this.to = to;
this.cost = cost;
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int s = sc.nextInt();
int t = sc.nextInt();
long e = sc.nextLong();
int maxNodeId = MAX_GALAXY_ID + n;
List<List<Edge>> adj = new ArrayList<>(maxNodeId + 1);
for (int i = 0; i <= maxNodeId; i++) {
adj.add(new ArrayList<>());
}
for (int i = 0; i < n; i++) {
long cost = sc.nextLong();
int k = sc.nextInt();
int networkNodeId = MAX_GALAXY_ID + i + 1;
for (int j = 0; j < k; j++) {
int galaxyId = sc.nextInt();
adj.get(galaxyId).add(new Edge(networkNodeId, cost));
adj.get(networkNodeId).add(new Edge(galaxyId, 0));
}
}
long[] dist = new long[maxNodeId + 1];
Arrays.fill(dist, INF);
PriorityQueue<long[]> pq = new PriorityQueue<>(Comparator.comparingLong(a -> a[0]));
dist[s] = 0;
pq.add(new long[]{0, s});
while (!pq.isEmpty()) {
long[] current = pq.poll();
long d = current[0];
int u = (int) current[1];
if (d > dist[u]) {
continue;
}
for (Edge edge : adj.get(u)) {
int v = edge.to;
long weight = edge.cost;
if (dist[u] + weight < dist[v]) {
dist[v] = dist[u] + weight;
pq.add(new long[]{dist[v], v});
}
}
}
if (dist[t] == INF || dist[t] > e) {
System.out.println(-1);
} else {
System.out.println(dist[t]);
}
}
}
import heapq
def solve():
MAX_GALAXY_ID = 50000
INF = float('inf')
n, s, t, e = map(int, input().split())
max_node_id = MAX_GALAXY_ID + n
adj = [[] for _ in range(max_node_id + 1)]
for i in range(n):
line = list(map(int, input().split()))
cost, k = line[0], line[1]
galaxies = line[2:]
network_node_id = MAX_GALAXY_ID + i + 1
for galaxy_id in galaxies:
adj[galaxy_id].append((cost, network_node_id))
adj[network_node_id].append((0, galaxy_id))
dist = [INF] * (max_node_id + 1)
pq = [(0, s)]
dist[s] = 0
while pq:
d, u = heapq.heappop(pq)
if d > dist[u]:
continue
for weight, v in adj[u]:
if dist[u] + weight < dist[v]:
dist[v] = dist[u] + weight
heapq.heappush(pq, (dist[v], v))
result = dist[t]
if result == INF or result > e:
print(-1)
else:
print(result)
solve()
算法及复杂度
- 算法:图建模 + Dijkstra 算法
- 时间复杂度:
,其中
是虫洞网络的数量,
是所有涉及到的星系的最大编号(本题中为 50000),
是所有网络中星系列表的长度总和。建图的时间复杂度为
。Dijkstra 算法的复杂度取决于节点和边的数量。节点总数为
,边的总数为
,因此 Dijkstra 的复杂度为
。
- 空间复杂度:
,主要用于存储图的邻接表和 Dijkstra 算法所需的距离数组。