洛谷 P1629 - 邮递员送信(往返两遍dijkstra)

该博客讨论了一个邮递员在带有单行道路的城市中寻找从邮局到所有目的地最短路径的问题。通过使用Dijkstra算法,分别解决单源最短路和多源单终点问题,以找到最小总时间。文章提供了C++代码实现,涉及图的链式前向星存储和堆优化。
摘要由CSDN通过智能技术生成


题目描述

有一个邮递员要送东西,邮局在节点 1 1 1。他总共要送 n − 1 n-1 n1 样东西,其目的地分别是节点 2 2 2 到节点 n n n。由于这个城市的交通比较繁忙,因此所有的道路都是单行的,共有 m m m 条道路。这个邮递员每次只能带一样东西,并且运送每件物品过后必须返回邮局。求送完这 n − 1 n-1 n1 样东西并且最终回到邮局最少需要的时间。

输入格式

第一行包括两个整数, n n n m m m,表示城市的节点数量和道路数量。

第二行到第 ( m + 1 ) (m+1) (m+1) 行,每行三个整数, u , v , w u,v,w u,v,w,表示从 u u u v v v 有一条通过时间为 w w w 的道路。

输出格式

输出仅一行,包含一个整数,为最少需要的时间。

样例 #1

样例输入 #1

5 10
2 3 5
1 5 5
3 5 6
1 2 8
1 3 8
5 3 4
4 1 8
4 5 3
3 5 6
5 4 2

样例输出 #1

83

提示

对于 30 % 30\% 30% 的数据, 1 ≤ n ≤ 200 1 \leq n \leq 200 1n200

对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 1 0 3 1 \leq n \leq 10^3 1n103 1 ≤ m ≤ 1 0 5 1 \leq m \leq 10^5 1m105 1 ≤ u , v ≤ n 1\leq u,v \leq n 1u,vn 1 ≤ w ≤ 1 0 4 1 \leq w \leq 10^4 1w104,输入保证任意两点都能互相到达。


  • 从邮局出发到各个邮寄点肯定是单源最短路问题,即用dijkstra,考虑到本题数据量,存图需要用带权的链式前向星,并且还要再需堆优化一下;而折返回来时就属于多源单终点问题,但仍可以套dijkstra做,但需要再建一个存反向边的图。

C++代码

#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
#define INF 0x3f3f3f3f
typedef pair<int, int> PII;     //first:与源点距离   second:结点编号

const int N = 1e5 + 10;

/**
 * @brief 两遍dijkstra,从邮局过去一遍是单源多点最短路,所以可按照原本建边的顺序直接dijkstra
 * 回来的时候是多源单终点问题,但也可以转换为为dijkstra做,只用反向再建一下边
 * <-------反向建边的时候注意设置一个+n的偏移量来防止建边冲突-------->
 */

int e[2 * N], ne[2 * N], h[2 * N], w[2 * N];
int dist[2 * N];
bool st[2 * N];
int n, m, idx = 0;
int res = 0;		//要走的所有路程
priority_queue<PII, vector<PII>, greater<PII>> heap;        //按照源点距离排的小根堆

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

void dijkstra(int s){
    memset(dist, INF, sizeof dist);
    dist[s] = 0;
    heap.push({0, s});

    while(heap.size()){
        PII t = heap.top();
        heap.pop();
        int ver = t.second, distance = t.first;
        if(st[ver])     continue;

        st[ver] = true;
        for(int i = h[ver];i != -1;i = ne[i]){
            int j = e[i];
            
            if(w[i] + distance < dist[j]){
                dist[j] = w[i] + distance;
                heap.push({dist[j], j});
            }
        }
    }
}

int main(){
    scanf("%d %d", &n, &m);
    memset(h, -1, sizeof h);

    while(m --){
        int u, v, z;
        scanf("%d %d %d", &u, &v, &z);
        add(u, v, z);

        add(v + n, u + n, z);               //反向再建一遍图,+ n对每个点做一个偏移
    }
    dijkstra(1);
    for(int i = 1;i <= n;i ++)      res += dist[i];

    dijkstra(1 + n);
    for(int i = 1 + n;i <= n + n;i ++)      res += dist[i];
    
    cout << res << endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值