最短距离--迪杰斯特拉算法(堆优化 + BFS)

本题可抽象为一个汇点到多个源点的最短路问题
新增一个点(超级源点),超级源点连向所有商店一条边,权值为0,再从超级源点做单源最短路。
即所有的商店作为起点,查询的村庄,做为终点。
新增一点作为超级源点
超级源点连向所有商店,权值为0
从超级源点开始做单源最短路

总结:
超级源点跟超级汇点是模拟出来的虚拟点,多用于图中:

  1. 同时有多个源点和多个汇点,建立超级源点和超级汇点
  2. 同时有多个源点和一个汇点,建立超级源点
  3. 同时有多个汇点和一个源点,建立超级汇点
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

const int N = 1e5 + 10, M = N*3;//无向图2N,但是有个超级源点再向每个点连接一条边,所以是3N;
int h[N], e[M], ne[M], idx, w[M];   
int a[N];
int n, m;
int dist[N];
bool st[N];

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

typedef pair<int, int> PII;

void dj()
{
    memset(dist, 0x3f, sizeof (dist));
    dist[0] = 0;    //起点是0,不是1,因为加了虚拟源点!
    //维护的是节点的距离,但是必须还要知道节点编号才好操作.
    //小根堆:加两个参数!
    priority_queue<PII, vector<PII>, greater<PII>> heap;
    heap.push({0, 0});   //起点入队!起点到起点的距离为0!
    while (heap.size())    //当堆不为空的时候
    {
        auto t = heap.top();
        heap.pop();
        int v = t.second;
        if (st[v]) continue;
        st[v] = true;   //表示这个点已经确定了最短路!
        
        for (int i=h[v]; ~i; i = ne[i])
        {
            int j = e[i];
            if (dist[j] > dist[v] + w[i])
            {
                dist[j] = dist[v] + w[i];
                heap.push({dist[j], j});
            }
        }
    }
    
}

int main()
{
    cin >> n >> m;
    memset(h, -1, sizeof h);
    while (m -- )
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        add(a, b, c);       //选择添加边!
        add(b, a, c);
    }
    int q;
    scanf("%d", &q);
    while (q --)    //以商店做为起点!
    {
        int x;
        scanf("%d", &x);
        add (0, x, 0);  //建立一个虚拟头结点,让虚拟源点指向所有的起点!
    }
    
    dj();   //从虚拟源点出发:做一遍djkstra算法!
    
    //再输入查询:查询的节点都是终点!以虚拟源点做起点,跑一遍dj,然后找出哪个起点(商店)到某一点最近
    int k;
    scanf("%d", &k);
    while (k --)
    {
        int x;
        scanf("%d", &x);
        cout << dist[x] << endl;
    }
    
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值