求最短路径问题——PTA作业“城市间紧急救援”“旅游规划”有感

首先,鄙人编程小白,并不了解算法,只是分享自己的一点见解,还请大佬勿喷~~~

我们都知道,求解图最短路径,有这几种常用方法——Dijkstra、Floyd、SPFA等等我主要想说的是Dijkstra算法,这个在《数据结构》书上提的。以下是我总结的迪杰斯特拉算法的几个特点:

1.这个算法的核心是用贪心算法实现,即每次都选取最短的路径,以此求得短的路径。

2.这个算法属于单源,即一个点(单源)到其他点的最短路径。Floyd算法属于多源,能求解任意一个点(多源)到其他点的最短路径,所以要求使用Dijkstra算法得有起点。

3.这个算法不能处理负权值的边的问题。

所以,我总结的求最小路径用Dijkstra算法的模板如下:

#include<stdio.h>//头文件,不用说了
#define inf 1000000//这里是定义一个相当于无穷大的值

int dis[1001][1001];//声明一个保留距离的数组
int vis[1001]={0};//初始化一个记录访问的数组
int n,m,s,d;//声明的全局变量
//核心算法
void Dijkstra()
{
    vis[s]=1;//标记开始节点被访问过
    for(int i=0;i<n;i++){
        //初始化最短路径的长度为无穷
        int min = inf;
        int f;
        for(int j=0;j<n;j++){
            //寻找开始节点s到下一个距离最短的未被访问的节点
            if(vis[j]==0&&dis[s][j]<min){
                min=dis[s][j];//更新最短距离
                f=j;//标记好下一个最短距离的节点
            }
        }
        vis[f]=1;//标记下一个节点被访问了
        for(int j=0;j<n;j++){
            //求解到达某一节点的最短路径
            if(vis[j]==0&&dis[s][j]>dis[s][f]+dis[f][j]){
                dis[s][j]=dis[s][f]+dis[f][j];//最短路径更新
            }
        }
    }
}
//主函数
int main()
{
    scanf("%d %d %d %d",&n,&m,&s,&d);//输入有n个节点,m条边,求解s节点到d节点的最短路径
    //初始化各点之间的距离为无穷,自己到自己的距离为0
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            if(i!=j){
              dis[i][j]  = inf;
              dis[j][i]  = inf;
            }
        }
    }
    //更新各点之间的距离,有边的为具体权值,无边就还是无穷
    for(int i=0;i<m;i++){
        int q,w,e;
        scanf("%d %d %d %d",&q,&w,&e);//输入节点q到节点w之间的距离为e
        dis[q][w] = e;
        dis[w][q] = e;
    }
    Dijkstra();//求解最短距离
    printf("%d ",dis[s][d]);//输出最短距离
}

最后,附上我通过PTA作业的代码,求指点

旅游规划

#include<stdio.h>
#define inf 1000000
int dis[1001][1001];
int mon[1001][1001];
int vis[1001]={0};

int n,m,s,d;

int Dijkstra()
{
   
    vis[s]=1;
    for(int i=0;i<n;i++){
        int min = inf;
        int f;
        for(int j=0;j<n;j++){
            if(vis[j]==0&&dis[s][j]<min){//寻找下一个距离最短的城市
                min=dis[s][j];
                f=j;//做好下一城市编号的标记
            }
        }

        vis[f]=1;//到达下一城市
        
        for(int j=0;j<n;j++){
            if(vis[j]==0&&dis[s][j]>dis[s][f]+dis[f][j]){//到达某一城市的最短路径
                dis[s][j]=dis[s][f]+dis[f][j];//最短路径更新
                mon[s][j]=mon[s][f]+mon[f][j];
            }
            else if(vis[j]==0&&dis[s][j]==dis[s][f]+dis[f][j]){//发现其他的最短路径
                if(mon[s][j]>mon[s][f]+mon[f][j]){//最便宜路径费用更新
                    mon[s][j]=mon[s][f]+mon[f][j];
                }
            }
        }
    }
}

int main()
{
    int people;
    scanf("%d %d %d %d",&n,&m,&s,&d);

    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            if(i!=j){
              dis[i][j]  = inf;
              dis[j][i]  = inf;
              mon[i][j]  = inf;
              mon[j][i]  = inf;
            }
        }
    }
    for(int i=0;i<m;i++){
        int q,w,e,r;
        scanf("%d %d %d %d",&q,&w,&e,&r);
        dis[q][w] = e;
        dis[w][q] = e;
        mon[q][w] = r;
        mon[w][q] = r;
    }
    Dijkstra();
    printf("%d %d",dis[s][d],mon[s][d]);

}

城市间紧急救援

#include<stdio.h>
#define inf 1000000
int dis[1001][1001];
int cnt[1001],vis[1001]={0},man[1001],final[1001],passed[1001];

int n,m,s,d;

int Dijkstra()
{
    cnt[s]=1;//开始时路径条数为1
    vis[s]=1;
    for(int i=0;i<n;i++){
        int min = inf;
        int f;
        for(int j=0;j<n;j++){
            if(vis[j]==0&&dis[s][j]<min){//寻找下一个距离最短的城市
                min=dis[s][j];
                f=j;//做好下一城市编号的标记
            }
        }

        vis[f]=1;//到达下一城市
        
        for(int j=0;j<n;j++){
            if(vis[j]==0&&dis[s][j]>dis[s][f]+dis[f][j]){//到达某一城市的最短路径
                dis[s][j]=dis[s][f]+dis[f][j];//最短路径更新
                
                passed[j]=f;//记录上一个城市编号
                cnt[j]=cnt[f];//拷贝到达上一个城市时的最短路径条数
                final[j]=final[f]+man[j];//到达某城市召集的全部救援队数量
            }
            else if(vis[j]==0&&dis[s][j]==dis[s][f]+dis[f][j]){//发现其他的最短路径
                cnt[j]=cnt[j]+cnt[f];//更新到达当前城市时的最短路径条数
                
                if(final[j]<final[f]+man[j]){//最多救援队数量更新
                    passed[j]=f;//记录所经过的上一个城市编号
                    final[j]=final[f]+man[j];//更新救援队总数
                }
            }
        }
    }
}

int main()
{
    int people;
    scanf("%d %d %d %d",&n,&m,&s,&d);
    for(int i=0;i<n;i++){
        scanf("%d",&people);
        man[i] = people;
        final[i] = man[i];
        cnt[i]=1;
    }
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            if(i!=j){
              dis[i][j]  = inf;
              dis[j][i]  = inf;
            }
        }
    }
    for(int i=0;i<m;i++){
        int q,w,e;
        scanf("%d %d %d",&q,&w,&e);
        dis[q][w] = e;
        dis[w][q] = e;
    }
    Dijkstra();
    
    printf("%d %d\n",cnt[d],final[d]+man[s]);
 
    int road[1001];
    int x=0,t=d;
    while(passed[t]!=0){//所经历的城市的从后往前的顺序
        road[x++]=passed[t];
        t=passed[t];
    }
    printf("%d",s);
    for(int i=x-1;i>=0;i--)
        printf(" %d",road[i]);
    printf(" %d",d);
}

PS:这篇博客讲解的更加丰富详细,有兴趣可以阅读一下https://www.cnblogs.com/hxsyl/p/3270401.html

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值