sdut 3363 数据结构实验之图论七:驴友计划

本文介绍了一种基于SPFA算法解决图论中最短路径问题的方法。通过具体实例讲解了如何寻找两个城市间的最短路径及最低费用路径,并提供了一个C语言实现的示例程序。

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

数据结构实验之图论七:驴友计划

Time Limit: 1000MS  Memory Limit: 65536KB
Problem Description
做为一个资深驴友,小新有一张珍藏的自驾游线路图,图上详细的标注了全国各个城市之间的高速公路距离和公路收费情况,现在请你编写一个程序,找出一条出发地到目的地之间的最短路径,如果有多条路径最短,则输出过路费最少的一条路径。
Input

连续T组数据输入,每组输入数据的第一行给出四个正整数N,M,s,d,其中N(2 <= N <= 500)是城市数目,城市编号从0~N-1,M是城市间高速公路的条数,s是出发地的城市编号,d是目的地的城市编号;随后M行,每行给出一条高速公路的信息,表示城市1、城市2、高速公路长度、收费额,中间以空格间隔,数字均为整数且不超过500,输入数据均保证有解。 

Output

在同一行中输出路径长度和收费总额,数据间用空格间隔。 

Example Input
1
4 5 0 3
0 1 1 20
1 3 2 30
0 3 4 10
0 2 2 20
2 3 1 20
Example Output
3 40
Hint
 
Author
xam 

提示

SPFA算法:

同dijkstra一样,Bellman-Ford也是处理单元最短路径的算法,可以处理负环问题。这个算法的根源思想在于:如有最短路,则每个顶点最多经过一次,这条路不超过n-1条边,长度为k的路可能由长度为k-1的路增加一条边得到。每一个最短路dis[i]可能由dis[j]+edge[i][j]得到,也可能不变。根据这个原理,这个过程就能容易地写出来。

for(i=1;i<V;i++)
for(j=0;j<E;j++)
if(dis[G[j].v]>dis[G[i].u]+G[i].c)
relax(G[j].u,G[j].v,G[j].c);
为什么能够判断负环?根据原理,每个最短路都不会超过n-1条边,如果说超过了n-1条边的话,那么就说明走了重点,即走了环,因此就可以知道里面产生了负环,才使得这个最短路无解,那么如何判断负环?其实操作完这个过程后只要再进行一次松弛操作,如果发现还有点松弛的话,那么就说明产生了负环,这个问题也就解决了。SPFA对这个算法很好地进行了优化。
优化的根源在于,能够改变最短路的路径都是之前成功松弛过的点,也就是说,如果一个点被成功地松弛了,那么这个点才有可能松弛其他边,没有被松弛的点,就说明这个点所记录的最短路径没有被改变,那么以这个点为起点的边都不可能被松弛,所以具体的实现就是造一个队列,把成功松弛的点放入队列之中等待松弛,直到队列中没有点了,说明整个最短路的过程就结束了。

这个算法的期望时间复杂度是O(kE),一般情况下,k是一个常数且很小,所以这个算法拥有快速求取最短路的作用。

示例程序

#include <stdio.h>
#include <string.h>
#define MAX 1000000007
int map[500][500][2],v1[500],d[500][2];
void spfa(int s,int n)
{
    int i,f=0,top=0,q[20000],num=MAX;
    memset(v1,0,sizeof(v1));
    for(i=0;n>i;i++)
    {
        d[i][0]=MAX;
        d[i][1]=MAX;
    }
    v1[s]=1;
    d[s][0]=0;
    d[s][1]=0;
    q[top]=s;
    top++;
    while(f<top)
    {
        v1[q[f]]=0;
        for(i=0;n>i;i++)
        {
            if(d[i][0]>d[q[f]][0]+map[q[f]][i][0])
            {
                d[i][0]=d[q[f]][0]+map[q[f]][i][0];
                d[i][1]=d[q[f]][1]+map[q[f]][i][1];
                if(v1[i]==0)
                {
                    v1[i]=1;
                    q[top]=i;
                    top++;
                }
            }
            else if(d[i][0]==d[q[f]][0]+map[q[f]][i][0]&&d[i][1]>d[q[f]][1]+map[q[f]][i][1])
            {
                d[i][1]=d[q[f]][1]+map[q[f]][i][1];
                if(v1[i]==0)
                {
                    v1[i]=1;
                    q[top]=i;
                    top++;
                }
            }
        }
        f++;
    }
}
int main()
{
    int t,i,i1,i2,n,m,s,d1,u,v,w,z;
    scanf("%d",&t);
    for(i=1;t>=i;i++)
    {
        scanf("%d %d %d %d",&n,&m,&s,&d1);
        for(i1=0;n>i1;i1++)
        {
            for(i2=0;n>i2;i2++)
            {
                if(i1==i2)
                {
                    map[i1][i2][0]=0;
                }
                else
                {
                    map[i1][i2][0]=MAX;
                }
            }
        }
        for(i1=1;m>=i1;i1++)
        {
            scanf("%d %d %d %d",&u,&v,&w,&z);
            if(map[u][v][0]>w)
            {
                map[u][v][0]=w;
                map[u][v][1]=z;
                map[v][u][0]=w;
                map[v][u][1]=z;
            }
        }
        spfa(s,n);
        printf("%d %d\n",d[d1][0],d[d1][1]);
    }
    return 0;
}


/***************************************************
Code Len: 2012B
Result: Accepted
Take time: 0ms
Take Memory: 316KB
Submit time: 2016-07-23 17:13:52
****************************************************/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值