Sunlight9

P4568 [JLOI2011] 飞行路线

Nov 27th, 2025
769
0
Never
1
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 4.15 KB | None | 0 0
  1. #include <bits/stdc++.h>
  2.  
  3. using namespace std;
  4.  
  5. // 定义无穷大常量,用于初始化距离数组
  6. constexpr int inf = 0x3f3f3f3f;
  7.  
  8. // 定义边的结构体,用于邻接表和优先队列
  9. struct Edge {
  10.     int u, w; // u: 边的目标节点 (target node), w: 边的权值 (cost)
  11.    
  12.     // 重载小于号运算符 <
  13.     // std::priority_queue 默认是大根堆(最大的元素在堆顶)
  14.     // 为了让它变成小根堆(最小权值的边在堆顶),我们需要反转比较逻辑
  15.     // 当当前边的权值 w 大于对方的权值 o.w 时,判定为“真”,使其沉底
  16.     bool operator< (const Edge& o) const {
  17.         return w > o.w;
  18.     }
  19. };
  20.  
  21. int main() {
  22.     // 关闭输入输出同步,加速 I/O 操作
  23.     ios::sync_with_stdio(false);
  24.     cin.tie(nullptr);
  25.    
  26.     int n, m, k, s, t;
  27.     // 输入:城市数 n,航线数 m,免费次数 k,起点 s,终点 t
  28.     cin >> n >> m >> k >> s >> t;
  29.    
  30.     // 坐标偏移处理:题目给出的城市编号是 0 ~ n-1
  31.     // 这里将其统一加 1,变为 1 ~ n,符合部分人的编程习惯(不加也可以,只要统一就行)
  32.     s++; t++;
  33.    
  34.     // 初始化邻接表
  35.     // 分层图共有 k+1 层(第0层到第k层)
  36.     // 总节点数约为 n * (k + 1),为了防止越界,开大一点点
  37.     vector<vector<Edge> > adj((k + 1) * n + 1);
  38.    
  39.     // 读取 m 条航线信息
  40.     for (int i = 1; i <= m; ++i) {
  41.         int u, v, w;
  42.         cin >> u >> v >> w;
  43.         u++; v++; // 同样将输入的节点编号加 1
  44.        
  45.         // 1. 建立第 0 层的原始边(完全不使用免费票的情况)
  46.         // 连接 u 和 v,权值为 w
  47.         adj[u].push_back({v, w});
  48.         adj[v].push_back({u, w});
  49.        
  50.         // 2. 遍历第 1 层到第 k 层,构建分层图的其余部分
  51.         for (int j = 1; j <= k; ++j) {
  52.             // --- 策略 A:同层移动(不使用免费票) ---
  53.             // 在第 j 层,连接 u 和 v,花费仍然是 w
  54.             // 节点编号映射规则:第 j 层的节点 u 编号为 j * n + u
  55.             adj[j * n + u].push_back({j * n + v, w});
  56.             adj[j * n + v].push_back({j * n + u, w});
  57.            
  58.             // --- 策略 B:跨层移动(使用免费票) ---
  59.             // 从第 j-1 层跨越到第 j 层,花费为 0
  60.             // 这代表:在当前这条航线上使用了 1 次免费机会
  61.            
  62.             // 从 u (第 j-1 层) -> v (第 j 层),权值 0
  63.             adj[(j - 1) * n + u].push_back({j * n + v, 0});
  64.            
  65.             // 从 v (第 j-1 层) -> u (第 j 层),权值 0
  66.             adj[(j - 1) * n + v].push_back({j * n + u, 0});
  67.         }
  68.     }
  69.    
  70.     // Dijkstra 算法标准模板
  71.     priority_queue<Edge> q;
  72.     // dist 数组初始化为无穷大
  73.     vector<int> dist((k + 1) * n + 1, inf);
  74.     // vis 数组标记节点是否已确定最短路
  75.     vector<bool> vis((k + 1) * n + 1);
  76.    
  77.     // 起点入队:起点 s 位于第 0 层,距离为 0
  78.     q.push({s, 0});
  79.     dist[s] = 0;
  80.    
  81.     while (!q.empty()) {
  82.         Edge now = q.top();
  83.         q.pop();
  84.         int u = now.u; // 当前节点编号(包含了层级信息)
  85.        
  86.         // 如果该节点已经处理过最优解,跳过
  87.         if (vis[u])
  88.             continue;
  89.         vis[u] = true;
  90.        
  91.         // 遍历所有邻接边
  92.         for (int i = 0; i < adj[u].size(); ++i) {
  93.             int v = adj[u][i].u; // 目标节点
  94.             int w = adj[u][i].w; // 边权
  95.            
  96.             // 松弛操作:如果通过 u 到达 v 的距离更短,则更新
  97.             if (dist[v] > dist[u] + w) {
  98.                 dist[v] = dist[u] + w;
  99.                 q.push({v, dist[v]});
  100.             }
  101.         }
  102.     }
  103.    
  104.     // 统计答案
  105.     // 最终到达终点 t 时,可能使用了 0 次、1 次 ... 或 k 次免费票
  106.     // 这些状态分别对应分层图中的 t, n+t, 2n+t ... k*n+t
  107.     // 取所有可能层级中终点距离的最小值
  108.     int ans = inf;
  109.     for (int i = 0; i <= k; ++i) {
  110.         ans = min(ans, dist[i * n + t]);
  111.     }
  112.    
  113.     cout << ans << "\n";
  114.    
  115.     return 0;
  116. }
Advertisement
Comments
  • User was banned
Add Comment
Please, Sign In to add comment