洛谷题单指南-最短路-P2865 [USACO06NOV] Roadblocks G

原题链接:https://www.luogu.com.cn/problem/P2865

题意解读:求次短路长度。

解题思路:

方法一、求最短路的同时更新次短路

设d1[],d2[]分别保存节点的最短路、次短路,利用Dijikstra算法,每次从优先队列中取距离起点最短的路径d、节点u,

如果d>d2[u],说明此时路径比次短路大,必然无法用来更新次短路和最短路,忽略;

枚举u的所有邻接点,设为v,u-v边长w

如果d1[v]>d+w,则更新次短路d2[v]=d1[v],更新最短路d1[v]=d+w;

否则如果d1[v]<d+ w同时d2[v]>d+w,则更新次短路d2[v]=d+w。

100分代码:

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

typedef pair<int, int> PII;
const int N = 5005, M = 200005;
int h[N], e[M], w[M], ne[M], idx;
int d1[N], d2[N]; //d1距离起点最短路,d2距离起点次短路
int n, m;

void add(int a, int b, int c)
{
    e[++idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx;
}

void dijikstra()
{
    memset(d1, 0x3f, sizeof(d1));
    memset(d2, 0x3f, sizeof(d2));
    d1[1] = 0;
    priority_queue<PII, vector<PII>, greater<PII>> pq;
    pq.push({0, 1});
    while(pq.size())
    {
        PII p = pq.top(); pq.pop();
        int u = p.second, d = p.first;
        if(d > d2[u]) continue; //剪枝,当前u的距离比次短路还大,说明u不可能用来更新相邻节点的次短路
        for(int i = h[u]; ~i; i = ne[i])
        {
            int v = e[i];
            if(d1[v] > d + w[i])
            {
                d2[v] = d1[v];
                d1[v] = d + w[i];
                pq.push({d1[v], v});
            }
            else if(d1[v] < d + w[i] && d2[v] > d + w[i])
            {
                d2[v] = d + w[i];
                pq.push({d2[v], v});
            }
            
        }
    }
}

int main()
{
    cin >> n >> m;
    memset(h, -1, sizeof(h));
    for(int i = 1; i <= m; i++)
    {
        int u, v, w;
        cin >> u >> v >> w;
        add(u, v, w), add(v, u, w);
    }
    dijikstra();
    cout << d2[n];
    return 0;
}

如果对以上代码中if(d > d2[u]) continue;不好理解,可以进一步细化:

将最短路、次短路的更新和标记独立开来,只要秉持一个原则:一个节点的最短路或次短路只能由最短路或者次短路来更新。

每次加入优先队列的节点需要区分是计算过最短路还是次短路,这样就可以用vis1、vis2分别来进行标记。

可以得到以下代码:

100分代码:

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

const int N = 5005, M = 200005;

struct Node
{
    int id; //节点编号
    int type; //1:最短路 2:次短路
    int dist; //距离起点的最短路或者次短路
    bool operator < (const Node & x) const
    {
        return dist > x.dist;
    }
};
int h[N], e[M], w[M], ne[M], idx;
int d1[N], d2[N]; //d1距离起点最短路,d2距离终点次最短路
bool vis1[N], vis2[N]; //vis1最短路标记,vis2次短路标记
int n, m;

void add(int a, int b, int c)
{
    e[++idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx;
}

void dijikstra()
{
    memset(d1, 0x3f, sizeof(d1));
    memset(d2, 0x3f, sizeof(d2));
    d1[1] = 0;
    priority_queue<Node> pq;
    pq.push({1, 1, 0});
    while(pq.size())
    {
        Node node = pq.top(); pq.pop();
        int u = node.id, type = node.type, dist = node.dist;
        if(type == 1 && vis1[u] || type == 2 && vis2[u]) continue;
        if(type == 1) vis1[u] = true;
        else vis2[u] = true;
        for(int i = h[u]; ~i; i = ne[i])
        {
            int v = e[i];
            if(d1[v] > dist + w[i]) //如果是更优的最短路
            {
                d2[v] = d1[v]; //最短路变成次短路
                pq.push({v, 2, d2[v]});
                d1[v] = dist + w[i]; //更新最短路
                pq.push({v, 1, d1[v]});
            }
            else if(d1[v] < dist + w[i] && d2[v] > dist + w[i]) //如果是更优的次短路
            {
                d2[v] = dist + w[i]; //更新次短路
                pq.push({v, 2, d2[v]});
            }
        }
    }
}

int main()
{
    cin >> n >> m;
    memset(h, -1, sizeof(h));
    for(int i = 1; i <= m; i++)
    {
        int u, v, w;
        cin >> u >> v >> w;
        add(u, v, w), add(v, u, w);
    }
    dijikstra();
    cout << d2[n];

    return 0;
}

方法二、从起点、终点分别跑一遍Dijikstra,再枚举

设d1[],d2[]分别表示距离起点的最短路、距离终点的最短路,

从起点、终点分别跑Dijikstra更新d1、d2,

枚举所有的边:u->v,长度w

求不等于起点到终点最短路的d1[u] + d2[v] + w的最小值,即是次短路。

100分代码:

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

typedef pair<int, int> PII;
const int N = 5005, M = 200005;
int h[N], e[M], w[M], ne[M], idx;
int d1[N], d2[N]; //d1距离起点最短路,d2距离终点次最短路
bool vis[N];
int n, m;

void add(int a, int b, int c)
{
    e[++idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx;
}

void dijikstra(int s, int d[N])
{
    memset(d, 0x3f, sizeof(d1));
    memset(vis, false, sizeof(vis));
    d[s] = 0;
    priority_queue<PII, vector<PII>, greater<PII>> pq;
    pq.push({0, s});
    while(pq.size())
    {
        PII p = pq.top(); pq.pop();
        int u = p.second;
        if(vis[u]) continue;
        vis[u] = true;
        for(int i = h[u]; ~i; i = ne[i])
        {
            int v = e[i];
            if(d[v] > d[u] + w[i])
            {
                d[v] = d[u] + w[i];
                pq.push({d[v], v});
            } 
        }
    }
}

int main()
{
    cin >> n >> m;
    memset(h, -1, sizeof(h));
    for(int i = 1; i <= m; i++)
    {
        int u, v, w;
        cin >> u >> v >> w;
        add(u, v, w), add(v, u, w);
    }
    dijikstra(1, d1);
    dijikstra(n, d2);

    int ans = INT_MAX;
    for(int u = 1; u <= n; u++)
    {
        for(int j = h[u]; ~j; j = ne[j])
        {
            int v = e[j];
            int len = d1[u] + d2[v] + w[j];
            if(len != d1[n]) ans = min(ans, len);
        }
    }
    cout << ans;
    
    return 0;
}

 

posted @ 2025-04-06 20:32  hackerchef  阅读(75)  评论(0)    收藏  举报