[JLOI2011] 飞行路线

GitHub跳转原题关系图返回列表

把免费次数作为分层状态,在 n(k+1) 个状态上运行 Dijkstra。

OJ: luogu

题目 ID: P4568

难度:提高

标签:分层图Dijkstra状态扩展python

日期: 2026-07-17 03:00

题意

无向图中最多让 k 条边免费,求起点到终点的最低花费。

思路

状态 (city, used) 表示已使用 used 次免费机会。走一条边既可付费留在本层,也可花一次机会以 0 代价进入下一层;所有新边权仍非负,直接运行 Dijkstra。

Python 知识

  • 二维列表 distance[used][city] 对应分层图。
  • 堆元组 (cost, city, used) 自然按费用排序。
  • min(layer[target] for layer in distance) 允许免费次数少于 k

代码

python
import sys
from heapq import heappop, heappush


input = sys.stdin.buffer.readline
n, edges, free_limit = map(int, input().split())
start, target = map(int, input().split())
graph = [[] for _ in range(n)]
for _ in range(edges):
    u, v, price = map(int, input().split())
    graph[u].append((v, price))
    graph[v].append((u, price))

infinity = 10**30
distance = [[infinity] * n for _ in range(free_limit + 1)]
distance[0][start] = 0
heap = [(0, start, 0)]
while heap:
    current, node, used = heappop(heap)
    if current != distance[used][node]:
        continue
    for neighbor, price in graph[node]:
        candidate = current + price
        if candidate < distance[used][neighbor]:
            distance[used][neighbor] = candidate
            heappush(heap, (candidate, neighbor, used))
        if used < free_limit and current < distance[used + 1][neighbor]:
            distance[used + 1][neighbor] = current
            heappush(heap, (current, neighbor, used + 1))
print(min(layer[target] for layer in distance))

原有 C++ 版本仍保留:

cpp
#include <bits/stdc++.h>
using namespace std;

const int MAXN = 10000 + 5;
const int MAXM = 50000 * 2 + 5;
const long long INF = (1LL << 60);

struct HeapNode {
    int u;
    int used;
    long long dist;

    bool operator < (const HeapNode &other) const {
        return dist > other.dist;
    }
};

int n, m, k;
int s, t;
int head[MAXN], to[MAXM], nxt[MAXM], w[MAXM], edge_cnt;
long long dist_arr[MAXN][15];
bool vis[MAXN][15];

void init_graph() {
    edge_cnt = 0;
    for (int i = 0; i < n; i++) {
        head[i] = 0;
    }
}

void add_edge(int u, int v, int len) {
    edge_cnt++;
    to[edge_cnt] = v;
    w[edge_cnt] = len;
    nxt[edge_cnt] = head[u];
    head[u] = edge_cnt;
}

void dijkstra() {
    for (int i = 0; i < n; i++) {
        for (int j = 0; j <= k; j++) {
            dist_arr[i][j] = INF;
            vis[i][j] = false;
        }
    }

    priority_queue<HeapNode> pq;
    dist_arr[s][0] = 0;
    pq.push({s, 0, 0});

    while (!pq.empty()) {
        HeapNode cur = pq.top();
        pq.pop();

        int u = cur.u;
        int used = cur.used;
        if (vis[u][used]) {
            continue;
        }
        vis[u][used] = true;

        for (int i = head[u]; i != 0; i = nxt[i]) {
            int v = to[i];

            long long nd = dist_arr[u][used] + w[i];
            if (nd < dist_arr[v][used]) {
                dist_arr[v][used] = nd;
                pq.push({v, used, nd});
            }

            // 这条航线可以免费坐一次,相当于跳到下一层且不增加代价。
            if (used < k && dist_arr[u][used] < dist_arr[v][used + 1]) {
                dist_arr[v][used + 1] = dist_arr[u][used];
                pq.push({v, used + 1, dist_arr[v][used + 1]});
            }
        }
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    cin >> n >> m >> k;
    cin >> s >> t;
    init_graph();

    for (int i = 1; i <= m; i++) {
        int u, v, len;
        cin >> u >> v >> len;
        add_edge(u, v, len);
        add_edge(v, u, len);
    }

    dijkstra();

    long long answer = INF;
    for (int used = 0; used <= k; used++) {
        if (dist_arr[t][used] < answer) {
            answer = dist_arr[t][used];
        }
    }

    cout << answer << '\n';

    return 0;
}

复杂度

时间 O(k(n+m)log(kn)),空间 O(kn+m)

总结

“最多使用若干次能力”通常把使用次数加入状态分层。