最短路径问题-迪杰斯特拉Dijkdtra

学习篇

求最短路径的基本思想:

按照最短路径的长度递增的次序,依次求得——源点到其余各点的最短路径!

假设,从源点到顶点v的最短路径是所有最短路径中长度最短者。
路径长度 最短的最短路径 的特点:
在这条路径上,必定只含有一条弧,并且这一条弧的权值最小。

简单来说:
一条最短路径比如:Start -> a->····-> End
在一个图中,找到与源点距离最短的第一个点①,即找到第一个最短路,
再找与源点距离第二短的点②,此时寻找的第二个点有两种方式,要么是源点直接到这点(前提是源点与这个点有连接),要么是经过①到这个点(前提是①与这个点有连接),则源点到该点的最短路为以上两种方式的最小值;
以此类推
找到与源点第x短的点,若此时的点为终点,则最短路径长度为x

举个例子:
在这里插入图片描述

迪杰斯特拉Dijkdtra算法:

假设s为刚刚求得的最短路节点,则:
Dist[k]=min( Dist[k] , Map[s][k] + Dist[s] );

邻接矩阵做法:请看 hdu 1874
堆优化做法:待补

注意:
有向和无向:题目一般会在 输入格式中的输入 a,b两城市的后面补充这样的语句
有向:(a way)
无向:(a,b之间有多条路)(a,b之间是一条双向道路)

水题集

hdu 1874:畅通工程续-单源点最短路

hdu 1874

#include <bits/stdc++.h>
#define ll long long
#define inf 0x7FFFFFFF
using namespace std;

//dis[i]=min(dis[i],dis[s]+Map[s][i]);

int Map[210][210],v[210],dis[210];
int main(){
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int n,m;
    while(cin>>n>>m){
        //初始化
        for(int i=0;i<n;i++){
            dis[i]=inf;v[i]=0;
            for(int j=0;j<n;j++){
                Map[i][j]=inf;//i==j的情况为什么不是0,看看状态转移方程想想为什么
            }
        }
        //填表
        for(int i=0;i<m;i++){
            int a,b,c;cin>>a>>b>>c;
            Map[b][a]=Map[a][b]=min(Map[a][b],c);//去重边!!
        }
        int s,e;cin>>s>>e;dis[s]=0;v[s]=1;//记得更新起点的状态(v dis)

        while(s!=e){
            int next,mi=inf;
            for(int i=0;i<n;i++){//遍历不是“起点”的点
                if(!v[i]){
                    if(Map[s][i]!=inf)
                        dis[i]=min(dis[i],dis[s]+Map[s][i]);更新状态 (dis)
                    if(dis[i]<mi){mi=dis[i];next=i;}//找最小值即下一个“起点”
                }
            }
            if(mi==inf)break;//没有路到终点了
            s=next;
            v[s]=1;//记得更新“起点”状态(v)
        }
        if(dis[e]==inf)cout<<"-1"<<endl;
        else cout<<dis[e]<<endl;

    }
    return 0;
}

hdu 2066:多源点

hdu 2066
多源点转化成单源点:
构造一个虚拟源点,再手动连接一下虚拟源点与题中的多起/终点;

#include <bits/stdc++.h>
#define ll long long
#define inf 0x7FFFFFFF
using namespace std;

//dis[i]=min(dis[i],dis[s]+Map[s][i]);

int Map[1010][1010],v[1010],dis[1010];
int main(){
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int T,S,D;
    while(cin>>T>>S>>D){
        for(int i=0;i<1010;i++){
            dis[i]=inf;v[i]=0;
            for(int j=0;j<1010;j++)
                Map[i][j]=inf;
        }
        int n=0;
        while(T--){
            int a,b,c;cin>>a>>b>>c;
            Map[b][a]=Map[a][b]=min(Map[a][b],c);
            n=max(n,max(a,b));
        }
        n++;
        while(S--){
            int st;cin>>st;
            Map[0][st]=Map[st][0]=0;
        }
        while(D--){
            int ed;cin>>ed;
            Map[ed][n]=Map[n][ed]=0;
        }
        int s=0,e=n;v[s]=1;dis[s]=0;
        while(s!=e){
            int mi=inf,next;
            for(int i=0;i<=n;i++){
                if(!v[i]){
                    if(Map[s][i]!=inf)
                        dis[i]=min(dis[i],dis[s]+Map[s][i]);
                    if(mi>dis[i]){mi=dis[i];next=i;}
                }
            }
            if(mi==inf)break;
            s=next;
            v[s]=1;
        }
        cout<<dis[e]<<endl;
    }
    return 0;
}

hdu 2680:多源点&&有向

hdu 2680

这个题wa了一次:在于没搞清题目怎样才算是有向还是无向,就顺势搞清了

题中“a way”指明是有向

#include <bits/stdc++.h>
#define ll long long
#define inf 0x7FFFFFFF
using namespace std;

//dis[i]=min(dis[i],dis[s]+Map[s][i]);

int Map[1010][1010],v[1010],dis[1010];
int main(){
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int n,m,e;
    while(cin>>n>>m>>e){
        for(int i=0;i<=n;i++){
            dis[i]=inf;v[i]=0;
            for(int j=0;j<=n;j++)
                Map[i][j]=inf;
        }
        while(m--){
            int a,b,c;cin>>a>>b>>c;
            Map[a][b]=min(Map[a][b],c);
        }
        int w;cin>>w;
        while(w--){
            int ed;cin>>ed;
            Map[0][ed]=0;
        }
        int s=0;v[s]=1;dis[s]=0;
        while(s!=e){
            int mi=inf,next;
            for(int i=0;i<=n;i++){
                if(!v[i]){
                    if(Map[s][i]!=inf)
                        dis[i]=min(dis[i],dis[s]+Map[s][i]);
                    if(mi>dis[i]){next=i;mi=dis[i];}
                }
            }
            if(mi==inf)break;
            s=next;
            v[s]=1;
        }
        if(dis[e]==inf)cout<<"-1"<<endl;
        else cout<<dis[e]<<endl;
    }
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值