【SPFA+拆点】速度限制

本文介绍了一种解决路径寻找问题的方法——使用SPFA算法结合拆点技巧来应对速度限制问题。在给定城市道路网络中,考虑到部分路段缺失速度限制信息的情况,通过SPFA算法求解从起点到终点的最快路径。

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

第2题     速度限制(speed.pas)

 

【问题描述】

    在这个繁忙的社会中,我们往往不再去选择最短的道路,而是选择最快的路线。开车时每条道路的限速成为最关键的问题。不幸的是,有一些限速的标志丢失了,因此你无法得知应该开多快。一种可以辩解的解决方案是,按照原来的速度行驶。你的任务是计算两地间的最快路线。

    你将获得一份现代化城市的道路交通信息。为了使问题简化,地图只包括路口和道路。每条道路是有向的,只连接了两条道路,并且最多只有一块限速标志,位于路的起点。两地A和B,最多只有一条道路从A连接到B。你可以假设加速能够在瞬间完成并且不会有交通堵塞等情况影响你。当然,你的车速不能超过当前的速度限制。

【输入】

    输入文件speed.in的第一行是3个整数N,M和D(2<=N<=150),表示道路的数目,用0..N-1标记。M是道路的总数,D表示你的目的地。接下来的M行,每行描述一条道路,每行有4个整数A(0≤A<N),B(0≤B<N),V(0≤V≤500)and L(1≤L≤500),这条路是从A到B的,速度限制是V,长度为L。如果V是0,表示这条路的限速未知。如果V不为0,则经过该路的时间T=L/V。否则T=L/Vold,Vold是你到达该路口前的速度。开始时你位于0点,并且速度为70。

【输出】

       输出文件speed.out仅一行整数,表示从0到D经过的城市。

       输出的顺序必须按照你经过这些城市的顺序,以0开始,以D结束。仅有一条最快路线。

【样例】

       speed.in                               speed.out

       6 15 1                                 0 5 2 3 1

       0 1 25 68

       0 2 30 50

       0 5 0 101

       1 2 70 77

       1 3 35 42

       2 0 0 22

       2 1 40 86

       2 3 0 23

       2 4 45 40

       3 1 64 14

       3 5 0 23

       4 1 95 8

       5 1 0 84

       5 2 90 64

       5 3 36 40


这道题是和以前WZZ讲过的一道题方法一样的一道题,SPFA+拆点,记得那道题是可以加速多少次的问题,一直没有做出来,这次终于是实现了。

这道题要松弛一个点,光知道两点边的权值是不行的,速度是未知的,而从不同点到线段的源点,速度是不一样的,于是在原来的dist的基础上,

需要多加一维,附加一个上一个点的信息。注意,记录方案时也需要增加一维,一开始我就错在了这里。


#include <cstdio>
#include <queue>
#include <cstdlib>
#include <cstring>


struct node
{
    long a;
    long b;
    long v;
    long l;
    node* next;
};


struct pos
{
    long i;
    long f;
    long sudu;
    pos(long ii,long ff,long ss):i(ii),f(ff),sudu(ss){}
};


std::queue<pos> que;
long n;long m;long destination;
node* head[160];
double d[160][160];
bool hash[160][160];
long fa[160][160];


void spfa()
{
    for (long i=0;i<n+1;i++)
        for (long j=0;j<n+1;j++)
            d[i][j] = 0x7fff0000;
    hash[0][0] = true;
    d[0][0] = 0;
    que.push(pos(0,0,70));


    while (!que.empty())
    {
        pos uu = que.front();
        long u = uu.i;
        que.pop();
        hash[uu.f][u] = false;
        for (node* vv=head[uu.i];vv;vv=vv->next)
        {
            long sudu = vv->v;
            if (vv->v <= 0) sudu = uu.sudu;
            long v = vv -> b;
            if (d[u][v]>d[uu.f][u]+double(vv->l)/double(sudu))
            {
                fa[u][v] = uu.f;
                d[u][v] = d[uu.f][u]+double(vv->l)/double(sudu);
                if (!hash[u][v])
                {
                    hash[u][v] = true;
                    que.push(pos(v,u,sudu));
                }
            }
        }
    }
}


void output(long u,long v)
{
    if (u==v&&v==0)
    {
        printf("0 ");
        return;
    }
    output(fa[u][v],u);
    printf("%ld ",v);
}


void insert(long a,long b,long v,long l)
{
    node* nn = new node;
    nn->a = a;
    nn->b = b;
    nn->v = v;
    nn->l = l;
    nn->next = head[a];
    head[a] = nn;
}


int main()
{
    freopen("speed.in","r",stdin);
    freopen("speed.out","w",stdout);
    scanf("%ld%ld%ld",&n,&m,&destination);
    for (long i=1;i<m+1;i++)
    {
        long a;long b;long v;long l;
        scanf("%ld%ld%ld%ld",&a,&b,&v,&l);
        insert(a,b,v,l);
    }
    spfa();
    double min = 0x7fff0000;
    long ans = 0;
    for (long i=0;i<n+1;i++)
    {
        if (min > d[i][destination])
        {
            ans = i;
            min = d[i][destination];
        }
    }
    output(ans,destination);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值