[USACO06NOV] Roadblocks G(洛谷P2865)

题目描述

Bessie 搬到了一个小农场,有时喜欢回去拜访她的一个好朋友。她不想太快到达她的旧家,因为她喜欢沿途的风景。她决定选择第二短的路径而不是最短的路径。她知道一定存在某条第二短路径。

乡村由 R(1≤R≤100,000) 条双向道路组成,每条道路连接 N(1≤N≤5000) 个交叉路口中的两个,这些交叉路口被方便地编号为 1 到 N。Bessie 从交叉路口 1 出发,她的朋友(目的地)在交叉路口 N。

第二短路径可以与任何最短路径共享道路,并且可以回溯,即多次使用相同的道路或交叉路口。第二短路径是长度比最短路径长的最短路径(即,如果存在两条或多条最短路径,第二短路径是长度比这些路径长但不比任何其他路径长的路径)。

输入格式

第 1 行:两个用空格分隔的整数:N 和 R。

第 2 行到第 R+1 行:每行包含三个用空格分隔的整数:A、B 和 D,描述连接交叉路口 A 和 B 的一条长度为 D(1≤D≤5000) 的道路。

输出格式

第 1 行:节点 1 和节点 N 之间第二短路径的长度。

显示翻译

题意翻译

输入输出样例

输入 #1复制运行

复制代码
4 4
1 2 100
2 4 200
2 3 250
3 4 100

输出 #1复制运行

复制代码
450

说明/提示

两条路径:1→2→4(长度 100+200=300)和 1→2→3→4(长度 100+250+100=450)。

(由 ChatGPT 4o 翻译)

1. 题目重述

题目名称:Roadblocks (USACO 06 NOV)

核心问题 :给定一张N个点、R条边的双向图,求从起点1到终点N的严格次短路径长度。

定义 :严格次短路径是指长度严格大于最短路径长度的所有路径中,长度最小的那一条。

  • 数据范围

  • 特殊说明:路径可以包含重复的节点或边(即允许走回头路)。

2. 题目分析

误区:K 短路算法?

一听到"第 K 短路",很多人第一反应是 A* 算法。虽然 A* 可以解,但对于K=2且图比较简单的情况,A* 写起来太繁琐且容易出错。

正解:Dijkstra 状态拆分

普通的 Dijkstra 维护一个 dis[i] 表示起点到i的最短距离。

为了求次短路,我们只需要让每个节点多维护一个状态。

状态定义

  • dis[i][0]:起点到i的最短路长度。

  • dis[i][1]:起点到i的严格次短路长度。

初始化

  • dis[1][0]=0,其余全为

松弛操作(核心逻辑)

假设当前从点u拿出的距离是d,准备通过边权w更新邻居v,新距离

  1. 更新最短路

    如果

    • 原有的最短路沦为次短路,赋值给,并加入队列。

    • 成为新的最短路,赋值给,并加入队列。

  2. 更新次短路

    如果

    • 说明找到了一条比最短路长、但比旧次短路短的路径。

    • 更新,并加入队列。

  3. 等于最短路

    如果

    • 题目要求严格次短,所以相等的路径对次短路没有贡献,直接忽略。

3. 完整代码

cpp 复制代码
//严格次短路 邻接表+堆优化Dijkstra 时间复杂度:O(Rlog N)
#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>//对应min函数
using namespace std;
const int maxn=5010;
const int maxm=200010;
int dis[maxn][2];//dis[i][0]代表起点到i点的最短路 dis[i][1]代表次短路
int h[maxn];//存所有点的头指针
int vtex[maxm];//存第i条边终点下标
int nxt[maxm];//与第i条边同起点的下一条边的索引
int wt[maxm];//第i条边长度
int idx;//总边数
int n,r;
int vis[maxn];//记录每个点是否已经出队过
struct node{
    int id;//点编号
    int w;//点到起点距离(不管最短还是次短)
    //优先队列默认最大堆,重载运算符修改为最小堆
    friend bool operator <(node a,node b){
        return a.w>b.w;
    }
};
priority_queue<node> q;
void addedge(int u,int v,int w){
    vtex[idx]=v;
    nxt[idx]=h[u];
    wt[idx]=w;
    h[u]=idx++;
}

void dijkstra(){
    dis[1][0]=0;//起点到自己的最短距离为0
    node tmp;
    tmp.id=1;
    tmp.w=0;
    q.push(tmp);//起点入队
    while(!q.empty()){
        tmp=q.top();//访问队首
        q.pop();//队首出队
        int nid=tmp.id;
        //如果出队的tmp到起点的距离的距离大于tmp当前存储的到起点的次短路的距离 就跳过
        //因为这个距离不可能用来更新tmp的邻接点
        if(tmp.w>dis[tmp.id][1]) continue;
        int p=h[nid];
        while(p!=-1){//遍历tmp所有临接点
            //如果邻接点到起点的最短路的距离大于经tmp去到起点的距离(情况1 发现比最短路更短的路径)
            if(dis[vtex[p]][0]>tmp.w+wt[p]){
                //就先把邻接点到起点的最短路的距离给到次短路
                dis[vtex[p]][1]=dis[vtex[p]][0];
                //然后再更新邻接点到起点的最短路的距离等于经tmp去到起点的距离
                dis[vtex[p]][0]=tmp.w+wt[p];
                //把最短距离和次短距离都入队
                q.push({vtex[p],dis[vtex[p]][1]});
                q.push({vtex[p],dis[vtex[p]][0]});
            }
            //如果邻接点到起点的最短路距离小于经tmp去到起点的距离 但是邻接点到起点的次短路距离大于经tmp去到起点的距离(情况2发现严格次短路径 (介于最短和次短之间))
            else if(dis[vtex[p]][0]<tmp.w+wt[p] &&dis[vtex[p]][1]>tmp.w+wt[p]){
                //就更新邻接点到起点的次短路距离等于经tmp去到起点的距离
                dis[vtex[p]][1]=tmp.w+wt[p];
                //然后入队
                q.push({vtex[p],dis[vtex[p]][1]});
            }
            p=nxt[p];
        }
    }
}
int main(){
    cin>>n>>r;//n个交叉路口 r条路
    //初始化头指针数组为空
    memset(h,-1,sizeof(h));
    //初始化每个点到其他到起点的最短距离和次短距离都是无穷
    memset(dis,0x3f,sizeof(dis));
    //邻接表存图
    for(int i=1;i<=r;i++){
        int u,v,w;
        cin>>u>>v>>w;
        addedge(u,v,w);//双向道路 双向加边
        addedge(v,u,w);
    }
    dijkstra();
    //输出节点1和节点N之间第二短路径的长度
    cout<<dis[n][1];
    return 0;
}

4. 易错点总结

  1. 严格 :题目中的定义决定了代码逻辑。如果是"非严格(允许相等)",那么 new_dist > dis[v][0] 应该改为 new_dist>=dis[v][0] 或者在降级时逻辑会有变化。本题要求严格,所以相等的距离直接丢弃。

  2. 空间开销 :由于是双向边,addedge 调用了两次,所以边的数组 (vtex, nxt, wt) 大小必须是 。只开100000会导致 RE。

  3. 剪枝if(d>dis[u][1]) continue; 这句非常重要。因为一个点可能会被多次入队,如果取出的距离比我们已经找到的次短路还长,它就不可能更新任何点的最短或次短路了。

相关推荐
无极低码1 小时前
ecGlypher新手安装分步指南(标准化流程)
人工智能·算法·自然语言处理·大模型·rag
软件算法开发2 小时前
基于海象优化算法的LSTM网络模型(WOA-LSTM)的一维时间序列预测matlab仿真
算法·matlab·lstm·一维时间序列预测·woa-lstm·海象优化
superior tigre2 小时前
22 括号生成
算法·深度优先
努力也学不会java3 小时前
【缓存算法】一篇文章带你彻底搞懂面试高频题LRU/LFU
java·数据结构·人工智能·算法·缓存·面试
旖-旎4 小时前
二分查找(x的平方根)(4)
c++·算法·二分查找·力扣·双指针
ECT-OS-JiuHuaShan4 小时前
朱梁万有递归元定理,重构《易经》
算法·重构
智者知已应修善业5 小时前
【51单片机独立按键控制数码管移动反向,2片74CH573/74CH273段和位,按键按下保持原状态】2023-3-25
经验分享·笔记·单片机·嵌入式硬件·算法·51单片机
khddvbe5 小时前
C++并发编程中的死锁避免
开发语言·c++·算法
C羊驼5 小时前
C语言:两天打鱼,三天晒网
c语言·经验分享·笔记·算法·青少年编程
菜菜小狗的学习笔记5 小时前
剑指Offer算法题(四)链表
数据结构·算法·链表