最短路径问题 - 九度 OJ 1008

最短路径问题 - 九度 OJ 1008

题目

时间限制:1 秒 内存限制:128 兆 特殊判题:否
题目描述:
给你 n 个点,m 条无向边,每条边都有长度 d 和花费 p,给你起点 s 终点 t,要求输出起点到终点的最短距离及其花费,如果最短距离有多条路线,则输出花费最少的。
输入:
输入 n,m,点的编号是 1~n,然后是 m 行,每行 4 个数 a,b,d,p,表示 a 和 b之间有一条边,且其长度为 d,花费为 p。最后一行是两个数 s,t;起点 s,终点 t。n 和 m 为 0 时输入结束。(1<n<=1000, 0<m<100000, s != t)
输出:
输出 一行有两个数, 最短距离及其花费。
样例输入:
3 2
1 2 5 6
2 3 4 5
1 3
0 0
样例输出:
9 11
来源:
2010 年浙江大学计算机及软件工程研究生机试真题

在该题中不仅需要求得起点到终点的最短距离,还需要在有多条最短路径的时,选取花费最少的那一条。要解决这个问题,只要更改 Dijstra 算法中关于“更近”的评判标准即可:有两条路径,若它们距离不一样时,距离小的更近;若距离一样时花费少的更近。当定义这种新的评判标准后,Dijstra 算法照样能求得“最近”的路径长度。

#include <stdio.h>
#include <vector>

using namespace std;

struct E{//邻接链表元素结构体
    int next;
    int c;
    int cost;
};

vector<E> edge[1001];//邻接链表
int Dis[1001];//距离数组
int cost[1001];//花费数组
bool mark[1001];//是否属于集合K数组

int main()
{
    int n,m;
    int S,T;//起点,终点
    while(scanf("%d%d",&n,&m)!=EOF){
        if(n==0 && m==0)break;
        for(int i=1;i<=n;i++)edge[i].clear();//初始化邻接链表
        while(m--){
            int a,b,c,cost;
            scanf("%d%d%d%d",&a,&b,&c,&cost);
            E tmp;
            tmp.c=c;
            tmp.cost=cost;//邻接链表中增加了该边的花费信息
            tmp.next=b;
            edge[a].push_back(tmp);

            tmp.next=a;
            edge[b].push_back(tmp);
        }

        scanf("%d%d",&S,&T);//输入起点终点信息
        for(int i=1;i<=n;i++){//初始化
            Dis[i]=-1;
            mark[i]=false;
        }

        Dis[S]=0;
        mark[S]=true;
        int newP=S;//起点为S,将其加入集合K,且其最短距离确定为0
        for(int i=1;i<n;i++){
            for(int j=0;j<edge[newP].size();j++){
                int t=edge[newP][j].next;
                int c=edge[newP][j].c;
                int co=edge[newP][j].cost;//花费
                if(mark[t]==true)continue;
                if(Dis[t]=-1 || Dis[t]>Dis[newP]+c ||
                (Dis[t]=Dis[newP]+c && cost[t]>cost[newP]+co)){
                    //比较大小时,将距离相同但花费更短也作为更新的条件之一
                    Dis[t]=Dis[newP]+c;
                    cost[t]=cost[newP]+co;//更新花费
                }
            }

            int min=123123123;
            for(int j=1;j<=n;j++){
                //选择最小值,选择时不用考虑花费的因素,
                //因为距离最近的点的花费已经不可能由于经过其他点发生改变了
                if(mark[j]==true)continue;
                if(Dis[j]==-1)continue;
                if(Dis[j]<min){
                    min=Dis[j];
                    newP=j;
                }
            }
            mark[newP]=true;
        }
        printf("%d %d\n",Dis[T],cost[T]);//输出答案
    }
    return 0;
}

值得一提的是,若由结点 U 到结点 V 的最短路径不存在,即它们不连通,那么当 Dijstra 算法完成以后,V 结点仍然不属于集合 K。即当完成 Dijstra 算法后,mark[V]依然为 false 即说明,结点 U 到结点 V 的最短路不存在。
注:该最短路不存在,指结点 U 和 V 不连通的情况,我们不考虑存在负环的情况,边的权值为负这种特殊的情况在机试中考察的可能性不大,但若真的出现边的权值为负,若不存在负环则最短路存在,但我们不能使用 Dijstra 对其进行求解,因为 Dijstra算法原理在存在负权值的图上不成立;若存在负环则最短路不存在。要求解包含负权值边上的最短路问题,我们需要使用 SPFA 算法。

总结 Dijstra 算法的特点:它的时间复杂度为 O(N^2)(若在查找最小值处利用堆进行优化,则时间复杂度可以降到 O(N*logN),N 为结点的个数。空间复杂度为O(N)(不包括保存图所需的空间)。它同时适用于邻接矩阵和邻接链表形式保存的有向图和无向图。它求解从某一个特定的起点出发,到其它所有点的最短路径,即单源最短路径问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值