计蒜之道 2017 程序设计大赛 - 计蒜客 复赛 D 百度地图导航 最短路、Dijkstra的拓展

计蒜之道 2017 程序设计大赛 - 计蒜客 复赛 D 百度地图导航

  •  
  • 17.79%
  •  1000ms
  •  131072K


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

地图中有两类道路。第一类道路是 城市之间的快速路,两个城市 u,vu,v 之间增加一条距离为 cc 的边;第二类道路是城市群之间的高速路,连接两个城市群 a,ba,b,通过这条高速路,城市群 aa 里的每个城市与城市群 bb 里的每个城市之间两两增加一条距离为 cc 的边。图中所有边均为无向边。

你需要计算从城市 ss 到城市 tt 的最短路。

输入格式

第一行输入 n(1 \le n \le 20000),n(1n20000), m(0 \le m \le 20000)m(0m20000),分别表示城市总数和城市群总数。

接下来一共输入 mm 行。

第 ii 行首先输入一个 k_i(1 \le k_i \le n)ki(1kin),表示第 ii 个城市群中的城市数为 k_iki。接下来输入 k_iki 个数,表示第 ii 个城市群中每个城市的编号(保证一个城市群内的城市编号不重复且合法,\sum_{i=1}^{m}k_i \le 20000i=1mki20000)。

下一行输入一个整数 m_1(0 \le m_1 \le 20000)m1(0m120000),表示有 m_1m1 条第一类道路,即 城市之间的快速路

接下来 m_1m1 行,每行输入三个整数 u_i,v_i(1 \le u_i, v_i \le n),c_i(1 \le c_i \le 10^6)ui,vi(1ui,vin),ci(1ci106),分别表示快速路连接的两个城市编号和边的距离。

下一行输入一个整数 m_2(0 \le m_2 \le 20000)m2(0m220000),表示有 m_2m2 条第二类道路,即 城市群之间的高速路

接下来 m_2m2 行,每行输入三个整数 a_i,b_i(1 \le a_i, b_i \le m),l_i(1 \le l_i \le 10^6)ai,bi(1ai,bim),li(1li106),分别表示快速路连接的两个城市群编号和边的距离。

最后一行输入 s, t(1 \le s, t \le n)s,t(1s,tn),表示起点和终点城市编号。

输出格式

输出一个整数,表示城市 ss 到城市 tt 到最短路。如果不存在路径,则输出-1

样例说明

1 -> 2 - > 5或者1 -> 4 -> 5是最短的路径,总长度为 1212

样例输入
5 4
2 5 1
2 2 4
1 3
2 3 4
2
1 2 9
1 5 18
2
1 2 6
1 3 10
1 5
样例输出
12

Source
计蒜之道 2017 程序设计大赛 - 计蒜客 复赛 D 百度地图导航

计蒜客 15969 百度地图导航


My Solution
题意:有 n 个点,编号依次为 1 到 n,且有若干个点的集合,编号依次为 1 到 m。每个点集合包含一个或多个点;每个点可能属于多个点集合,也可能不属于任何点集合。
图中中有两种边。第一类边是点u,v 之间权值为ci的无向边;第二类边是点集合之间的无向边,连接两个点集合 a,b,通过这条边,城市群 a 里的每个点与点集合 b 里的每个点之间有一条权值为 w 的无向边。求从点s 到点 t 的最短路。


最短路、Dijkstra的拓展

这里n+m是4e4所以很可能只能用O(nlogn)的算法,所以可以用Dijkstra+堆优化来进行拓展,

这里把点集合 a作为点 a+n来进行存储,然后dis[i]表示当前从起点s到点或者点集合i的最短路权值。

然后建立这个点集合到点的映射 vector from[a], 和点到它所在的集合的映射 vector to[u]。

然后跑Dijkstra的时候,如果从小根堆取出的点是表示普通的点则和普通的Dijkstra一样跑,

然后对于这个点映射出的点集合也跑一边松弛操作,

       szto = to[u].size();
       for(j = 0; j < szto; j++){
            sz = sons[to[u][j]].size();
            uu = to[u][j];
            for(i = 0; i < sz; i++){
                v = sons[uu][i].first;
                w = sons[uu][i].second;
                if(d + w < dis[v]){
                    dis[v] = d + w;
                    pq.push(ii(dis[v], v));
                }
            }
}

如果 u>n则表示这是一个点集合,在进行常规松弛操作之后,还要把dis[u]更新到所有的 点集合u映射到的点,

       sz = from[u].size();
for(i = 0; i < sz; i++){
            v = from[u][i];
            if(d < dis[v]){
                dis[v] = d;
                pq.push(ii(dis[v], v));
            }
        }

跑完Dijkstra后答案就是dis[destination]了。

时间复杂度 O((n+m)log(n+m))

空间复杂度 O(N)


#include <iostream>
#include <cstdio>
#include <queue>
#include <vector>

#include <cstring>
using namespace std;
typedef long long LL;
typedef pair<LL, LL> ii;
const int MAXN = 4e4 + 8;
const LL INF = 9e18 + 8;

vector<int> to[MAXN], from[MAXN];

vector<ii> sons[MAXN];
LL dis[MAXN];
bool vis[MAXN];
priority_queue<ii, vector<ii>, greater<ii> > pq;
//O(nlogn)
inline void dijkstra(int n, int src)
{
    for(int i = 1; i <= n; i++){
        dis[i] = INF;
    }
    memset(vis, false, sizeof vis);
    dis[src] = 0;
	while(!pq.empty()) pq.pop();
	pq.push(ii(0, src));
	LL u, v, w, d, sz, i, j, szto, uu;
	while(!pq.empty()){

        u = pq.top().second;
        d = pq.top().first;//cout << u << " " << d << endl;
        pq.pop();
        if(vis[u]) continue;
        vis[u] = true;
        //
        sz = sons[u].size();
		for(i = 0; i < sz; i++){
			v = sons[u][i].first;
			w = sons[u][i].second;
			if(d + w < dis[v]){
				dis[v] = d + w;
				pq.push(ii(dis[v], v));
			}
		}
		//
		szto = to[u].size();
		for(j = 0; j < szto; j++){
            sz = sons[to[u][j]].size();
            uu = to[u][j];
            for(i = 0; i < sz; i++){
                v = sons[uu][i].first;
                w = sons[uu][i].second;
                if(d + w < dis[v]){
                    dis[v] = d + w;
                    pq.push(ii(dis[v], v));
                }
            }
		}
		//
		sz = from[u].size();
		for(i = 0; i < sz; i++){
            v = from[u][i];
            if(d < dis[v]){
                dis[v] = d;
                pq.push(ii(dis[v], v));
            }
        }
	}
}

int main()
{
    #ifdef LOCAL
    freopen("d.txt", "r", stdin);
    //freopen("d.txt", "w", stdout);
    int T = 1;
    while(T--){
    #endif // LOCAL
    ios::sync_with_stdio(false); cin.tie(0);

    LL n, m, k, i, j, m1, m2, src, dest, e, u, v, w, ans = INF;
    cin >> n >> m;
    for(i = 1; i <= m; i++){
        cin >> k;
        for(j = 0; j < k; j++){
            cin >> u;
            to[u].push_back(i+n);
            from[i+n].push_back(u);
        }
    }
    cin >> m1;
    while(m1 && m1--){
        cin >> u >> v >> w;
        sons[u].push_back(ii(v, w));
        sons[v].push_back(ii(u, w));
    }
    cin >> m2;
    while(m2 && m2--){
        cin >> u >> v >> w;
        sons[u+n].push_back(ii(v+n, w));
        sons[v+n].push_back(ii(u+n, w));
    }

    cin >> src >> dest;

    dijkstra(n+m, src);
    ans = dis[dest];
    /*
    int sz = to[dest].size(), szfrom, uu;
    for(i = 0; i < sz; i++){
        uu = to[dest][i];
        szfrom = from[uu].size();
        for(j = 0; j < szfrom; j++){
            ans = min(ans, dis[from[uu][j]]);
        }
    }
    */

    if(ans == INF) cout << -1 << endl;
    else cout << ans << endl;

    #ifdef LOCAL
    cout << endl;
    }
    #endif // LOCAL
    return 0;
}


  Thank you!

                                                                                                                                            ------from ProLights

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值