计蒜客 百度地图导航(Dijkstra&超级源点与超级汇点)

百度地图上有 n 个城市,城市编号依次为 1 到 n。地图中有若干个城市群,编号依次为 1 到 m。每个城市群包含一个或多个城市;每个城市可能属于多个城市群,也可能不属于任何城市群。

地图中有两类道路。第一类道路是 城市之间的快速路,两个城市 u,v 之间增加一条距离为 c 的边;第二类道路是 城市群之间的高速路,连接两个城市群 a,b,通过这条高速路,城市群 a 里的每个城市与城市群 b 里的每个城市之间两两增加一条距离为 c 的边。图中所有边均为无向边。
计算从城市 s 到城市 的最短路。

这道题思路很明了,建好图单源最短路跑一遍就可以了,但是关键在于建图。城市与城市之间的边很好建,直接加上去。但是如果建城市群的边的话就会比较麻烦,要写个二重循环,一一对应加边,倘若一个城市群有u个城市另一个有v个,时间复杂度达到O(nm)是巨大的.所以要转变加边的思路.

如果有两个城市群uv,u种有abc三个城市,v中有def三个城市。按照一一对应的加边方法,建图如下:
一一对应的加边方法
随着城市群中城市数量的加多,边的对应关系会越来越复杂。所以我们用两个特殊点分别作为城市群的入点和出点。添加城市群的边只要更改这两个点就可以了(x进入y出去)添加两个点
那么如何添加城市群的边呢?我们只要把u的出点指向v的入点,再把v的出点指向u的如电就行了在这里插入图片描述
像这样,随着城市群里面的城市增加,优化效果是显而易见的。
但是我们添加了点,所以要为点编号,1-n是不可用的,所以我们把入点编为n+u,出点边为2n+u

//建图用了链式前向星
#include<iostream>
#include<cstdio>
#include<vector>
#include<cstdio>
#include<queue>
#pragma comment(linker, "/STACK:102400000,102400000")   //防止栈溢出
using namespace std;
const int maxn = 5e5;
const long long inf = 1e18;
int n,m,cnt,m1,m2;
int u,v,w,s,t,k,temp;
int head[maxn],vis[maxn];
long long dis[maxn];
vector<int>vec[maxn];
struct edge{
    int to,next;
    long long w;
}e[maxn*10];
struct node{
    long long dis;
    int pos;
    bool operator < (const node & a) const{
        return a.dis < dis;
    }
};
void addedge(int u,int v,long long w){  //加边函数
    e[++cnt].to = v;
    e[cnt].w = w;
    e[cnt].next = head[u];
    head[u] = cnt;
}
priority_queue<node>q;  //优先队列
void dijkstra(int s){   //堆优化的Dijkstra
    dis[s] = 0;
    q.push((node){0,s});
    while(!q.empty()){
        node temp = q.top();
        q.pop();
        int x = temp.pos;
        if(vis[x])  continue;
        vis[x] = 1;
        for(int i = head[x];i;i=e[i].next){
            int y = e[i].to;
            if(dis[y] > dis[x] + e[i].w){
                dis[y] = dis[x] + e[i].w;
                if(!vis[y]) q.push((node){dis[y],y});
            }
        }
    }

}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i = 0;i <= 2*n+m;i++)
        dis[i] = inf;
    for(int i = 1;i <= m;i++){
        scanf("%d",&k);
        while(k--){ //用vector存城市群
            scanf("%d",&temp);
            vec[i].push_back(temp);
        }
    }
    for(int i = 1;i <= m;i++){  //这是为城市群添加入点和出点
        for(int j = 0;j < vec[i].size();j++){
            addedge(i+n,vec[i][j],0);
            addedge(vec[i][j],i+2*n,0);
        }
    }
    scanf("%d",&m1);
    while(m1--){
        scanf("%d%d%d",&u,&v,&w);
        addedge(u,v,w);
        addedge(v,u,w);
    }
    scanf("%d",&m2);
    while(m2--){    //为城市群加边
        scanf("%d%d%d",&u,&v,&w);
        addedge(v+2*n,u+n,w);
        addedge(u+2*n,v+n,w);
    }

    scanf("%d%d",&s,&t);    //起点和终点
    dijkstra(s);
    printf("%lld\n",(dis[t]>=inf?-1:dis[t]));
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值