Wannafly挑战赛2 B.Travel【思维+SPFA】

本文介绍了一种解决精灵王国中寻找城市间最短路径问题的算法。通过分析环形城市布局与传送门连接特点,利用单源最短路径算法(SPFA),实现了对特定条件下最短路径的有效求解。

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

题目描述

精灵王国有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行每行一个正整数表示该次旅行的最短时间。
示例1

输入

4 1
1 2 3 6
1 3 2
5
1 2
1 4
1 3
2 3
4 3

输出

1
5
2
2
3

备注:

1 ≤ N、Q ≤ 52501,1 ≤ M ≤ 20,1 ≤ u[i]、v[i]、x、y ≤ N,1 ≤ d[i]、w[i] ≤ 2^(30)

思路:


观察数据范围,M很小,只有20,很容易把人往状压压缩上去带,其实并无相关性,我们只要找到问题的关键即可。

首先考虑,如果M为0的话,我们的图就是一个环,如果图只是一个环的话,那么从x到y的最短路就只有两种走法,取最小即可。

如果M>0的话,我们可能会存在从x到y的捷径,但是具体我们会经过哪些多出来的边我们并不要考虑,我们只要考虑从x到y的最短路,是否会经过这种边即可。


那么从x到y的最短路就只有两种情况:


①按照顺时针或者逆时针从x走到y

②在上述走法的基础上,再走至少一个传送门。

那么我们不妨做M*2个单源最短路,我们拿传送门的端点作为起点,去做单源最短路,那么对于②的情况,我们经过传送门了的情况,就是到了某个传送门端点的情况。

那么此情况,就相当于取Min(dist【传送门某端点v】【x】+dist【传送门某端点v】【y】);


对于dist数组的开取,离散化一下点就行了。


Ac代码:

#include<stdio.h>
#include<string.h>
#include<queue>
#include<vector>
#include<map>
using namespace std;
#define ll long long int
struct node
{
    ll from;
    ll to;
    ll next;
    ll w;
}e[1500000];
ll cont,n,m;
ll head[105000];
ll vis[105000];
ll dist[60][105000];
void add(ll from,ll to,ll w)
{
    e[cont].to=to;
    e[cont].w=w;
    e[cont].next=head[from];
    head[from]=cont++;
}
void SPFA(ll u,ll d)
{
    queue<ll>s;
    memset(vis,0,sizeof(vis));
    for(ll i=1;i<=n;i++)dist[d][i]=1000000000000000000;
    s.push(u);dist[d][u]=0;
    while(!s.empty())
    {
        ll u=s.front();s.pop();
        vis[u]=0;
        for(ll i=head[u];i!=-1;i=e[i].next)
        {
            ll v=e[i].to;
            ll w=e[i].w;
            if(dist[d][v]>dist[d][u]+w)
            {
                dist[d][v]=dist[d][u]+w;
                if(vis[v]==0)
                {
                    vis[v]=1;
                    s.push(v);
                }
            }
        }
    }
}
ll dd[105000];
int main()
{
    while(~scanf("%lld%lld",&n,&m))
    {
        memset(dd,0,sizeof(dd));
        ll summ=0;
        cont=0;
        memset(head,-1,sizeof(head));
        for(ll i=1;i<=n;i++)
        {
            ll len;scanf("%lld",&len);
            if(i!=n)add(i,i+1,len),add(i+1,i,len);
            else add(1,n,len),add(n,1,len);
            summ+=len;
            dd[i]=len+dd[i-1];
        }
        ll cnt=0;
        map<ll,ll>s;
        vector<ll>sou;
        for(ll i=1;i<=m;i++)
        {
            ll x,y,w;
            scanf("%lld%lld%lld",&x,&y,&w);
            if(s[x]==0)s[x]=++cnt;
            if(s[y]==0)s[y]=++cnt;
            sou.push_back(x);
            sou.push_back(y);
            add(x,y,w);
            add(y,x,w);
        }
        for(ll i=0;i<sou.size();i++)
        {
            ll sss=sou[i];
            SPFA(sss,s[sss]);
        }
        ll q;scanf("%lld",&q);
        for(ll i=1;i<=q;i++)
        {
            ll ans=1000000000000000000;
            ll x,y;scanf("%lld%lld",&x,&y);
            if(x>y)swap(x,y);
            for(ll j=0;j<sou.size();j++)
            {
                ans=min(ans,dist[s[sou[j]]][x]+dist[s[sou[j]]][y]);
            }
            ans=min(ans,dd[y-1]-dd[x-1]);
            ans=min(ans,summ-(dd[y-1]-dd[x-1]));
            printf("%lld\n",ans);
        }
    }
}









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值