[noip模拟赛]旅行Pod(spfa)

本文介绍了一种解决公交路径最短时间问题的算法实现,通过将公交线路转化为图中的有向边,并考虑发车间隔及等待时间,利用SPFA算法求解。详细解析了时间计算的复杂细节。

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

题目描述

这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

题解

一眼看上去就是最短路嘛,只不过有一点变形而已。
将所有的边都拆成正反有向边,对于每一条边<u,v>记录一下信息:边权c,路线k,0/1分别表示路线k正向或反向。
预处理t[i][j][k]表示第i个点,从第j条路线的起点/终点(k=0/1)跑到需要跑多少时间。
然后跑spfa。每一次更新的时候,根据枚举到的边的信息,算出在该点最少还需要等多长时间能搭上通向这条路的车,其余的正常做就行。
跑spfa的时候就是计算时间的时候比较容易出错。假设起点到当前点花的时间为pt1,某一趟列车从它的起点到这个点需要跑pt2的时间,列车的周期为T的话,那么需要等的时间为T(((pt1pt2)%60+60)%60)%T,即要等到pt1+T(((pt1pt2)%60+60)%60)%T这个时间再出发。注意这里的时间是以整点计算的,即在起点处pt1=mx。
还需要注意的一点是,当pt1=pt2时,等待的时间应该为0,但是用上面的式子计算出来应该是T,也就是恰好多了一个周期。这种情况需要特判一下。

代码

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
#define N 1005
#define E 10005

int n,k,x,y,gx,mx,s;
struct ANS{int g,m;}ans;
int C[N*2],st[N*2],p[N],r[N],t[N][N*2][2],dis[N];
int tot,point[N],nxt[E*2],v[E*2],c[E*2],K[E*2],ty[E*2];
bool vis[N];
queue <int> q;

inline void addedge(int x,int y,int z,int k,int i)
{
    ++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; c[tot]=z; K[tot]=k; ty[tot]=i;
}
inline void spfa()
{
    memset(dis,127/3,sizeof(dis)); dis[x]=mx;
    memset(vis,0,sizeof(vis)); vis[x]=true;
    while (!q.empty()) q.pop(); q.push(x);

    while (!q.empty())
    {
        int now=q.front(); q.pop();
        vis[now]=false;
        for (int i=point[now];i;i=nxt[i])
        {   
            int pt1=dis[now];
            int pt2=t[now][K[i]][ty[i]];
            int T=C[K[i]];
            int waiting,waito;

            waiting=T-(((pt1-pt2)%60+60)%60)%T;
            if (waiting==T) waiting=0;
            waito=pt1+waiting;

            if (waito+c[i]<dis[v[i]])
            {
                dis[v[i]]=waito+c[i];
                if (!vis[v[i]])
                {
                    vis[v[i]]=true;
                    q.push(v[i]);
                }
            }
        }
    }
}
inline ANS calc(int g,int m,int len)
{
    ANS ans;
    m+=len;
    g+=m/60;
    m%=60;
    g%=24;
    return ans=(ANS){g,m};
}
int main()
{
    freopen("pod.in","r",stdin);
    freopen("pod.out","w",stdout);
    scanf("%d%d%d%d%d%d",&n,&k,&x,&y,&gx,&mx);
    for (int i=1;i<=k;++i)
    {
        scanf("%d%d",&s,&C[i]);
        for (int j=1;j<=s;++j) scanf("%d",&p[j]);
        for (int j=1;j<s;++j) scanf("%d",&r[j]);

        for (int j=1;j<s;++j)
        {
            addedge(p[j],p[j+1],r[j],i,0);
            addedge(p[j+1],p[j],r[j],i,1);
        }
        for (int j=2;j<=s;++j)
            t[p[j]][i][0]=t[p[j-1]][i][0]+r[j-1];
        for (int j=s-1;j>=1;--j)
            t[p[j]][i][1]=t[p[j+1]][i][1]+r[j];
    }
    spfa();

    ans=calc(gx,mx,dis[y]-mx);
    printf("%d %d\n",ans.g,ans.m);
}

总结

1、这道题计算时间的细节非常值得推敲,以后细节要格外注意,想得更清楚一些。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值