Wannafly 挑战赛第二场 B Travel【最短路 建图】

本文介绍了一种解决精灵王国中寻找城市间最短路径问题的算法。通过预处理额外边端点间的最短路径,结合环形城市与传送门的特点,实现了高效的路径查找。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:https://www.nowcoder.com/acm/contest/17/B
题意:
题目描述
精灵王国有N座美丽的城市,它们以一个环形排列在Bzeroth的大陆上。其中第i座城市到第i+1座城市花费的时间为d[i]。特别地,第N座城市到第1座城市花费的时间为d[N]。这些道路都是双向的。
另外,精灵们在数千年的时间里建造了M座传送门,第i座传送门连接了城市u[i]与城市v[i],并且需要花费w[i]的时间通过(可能有两座传送门连接了同一对城市,也有可能一座传送门连接了相同的两座城市)。这些传送门都是双向的。
小S是精灵王国交通部部长,她的职责是为精灵女王设计每年的巡查路线。每次陛下会从某一个城市到达另一个城市,沿路调查每个城市的治理情况,她需要找出一条用时最短的路线。
输入描述:
第一行为2个整数N、M。
第二行为N个正整数d[i]。
接下来M行每行三个正整数u[i]、v[i]、w[i]。
第M+3行为一个正整数Q,表示需要设计路线的次数。
接下来Q行每行两个正整数x、y,表示一次从城市x到城市y的旅行。
输出描述:
Q行每行一个正整数表示该次旅行的最短时间。

wls的题解:
通过一些观察可得:节点 u 到节点 v 的最短路肯定为以下两种情况之一: 1、不经过额外的 m 条边直接从 u 到 v。
2、从 u 先走到某个为额外边端点的 x,再从 x 走到某个同样为额外边端点的 y,最后从
y 走到 v。
预处理所有额外边的端点间两两最短路,询问时暴力枚举 x、y 更新答案即可。
时间复杂度:O(M3 +N+Q× M3)。

代码:

#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
typedef long long ll;
const int MAXN=100005;
const int MAXM=25;
const ll INF=(1LL<<60)-1;
int u[MAXM],v[MAXM],key[MAXM<<1];
ll d[MAXN],p[MAXN],w[MAXM];
vector<pair<int,ll> > e[MAXN];
ll dis[MAXM<<1][MAXN];
void Dijkstra(int id,int n)
{
    int st=key[id];
    for(int i=1;i<=n;i++)
        dis[id][i]=INF;
    priority_queue<pair<ll,int> > pq;
    pq.push(make_pair(dis[id][st]=0,st));
    while(!pq.empty())
    {
        int u=pq.top().second;
        pq.pop();
        for(int j=0;j<e[u].size();j++)
        {
            pair<ll,int> t=e[u][j];
            int v=t.first,c=t.second;
            if(dis[id][v]>dis[id][u]+c)
            {
                dis[id][v]=dis[id][u]+c;
                pq.push(make_pair(-dis[id][v],v));
            }
        }
    }
}
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&d[i]);
        e[i].push_back(make_pair(i%n+1,d[i]));
        e[i%n+1].push_back(make_pair(i,d[i]));
    }
    for(int i=1;i<=n;i++)
        p[i]=p[i-1]+d[i];
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%lld",&u[i],&v[i],&w[i]);
        e[u[i]].push_back(make_pair(v[i],w[i]));
        e[v[i]].push_back(make_pair(u[i],w[i]));
    }
    int c=0;
    for(int i=1;i<=m;i++)
        key[++c]=u[i],key[++c]=v[i];
    sort(key+1,key+c+1);
    c=unique(key+1,key+c+1)-(key+1);
    for(int i=1;i<=c;i++)
        Dijkstra(i,n);
    int q;
    scanf("%d",&q);
    for(int i=1;i<=q;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        ll len=abs(p[x-1]-p[y-1]);
        ll res=min(len,p[n]-len);
        for(int j=1;j<=c;j++)
            res=min(res,dis[j][x]+dis[j][y]);
        printf("%lld\n",res);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值