7-1 最短路径之Floyd(重在原理而不在题目)

请缩写程序,实现求有向网的最短路径的Floyd算法,因地点可能够是淡泊湖,宁静楼等等,这里简化成编号,要求能够查询出任意两点(用编号表示)间的最短路径及最小距离。

说明:1)顶点个数上限不超过100;2)距离权值上限不超过9998;

输入格式:

输出格式:

 

输入样例:

第一行,两个整数:vN和eN(图中顶点数和和边数)。
接下来是vN条边的信息:起点,终点,权值(三个数一行,以空格分隔);
最后两行是待查询的两组起点与终点的编号(每行一个查询,起点与终点编号间也是空格分隔)。

6 8
0 5 100
0 2 10
0 4 30
1 2 5
2 3 50
3 5 10
4 3 20
4 5 60
0 5
2 0

(三块信息:顶点数和边数;边的信息;两组需要查询的顶点信息)

输出样例:

共输出三行,前两行分别输出两组查询的结果:先是起点到终点的路径说明,再是距离,中间以冒号分隔。如果两个点间不可达,输出距离-1。最后一行是整个可达距离中最大距离的描述(若有多组,输入位置稍前的一组,注意:不包括不可到达的无穷大)。

0->4->3->5:60
2->0:-1
1->2->3->5:65

 关于Floyd算法:

该算法重点在于解决起点终点都不确定的最短路径的问题,其实在之前的dijkstra算法也可以实现对应的目的,但是实操起来代码量较大且代码结构性不够美观(数据结构老师原话,哈哈),所以有了这个算法,那么下面我就来介绍一下Floyd算法实现原理:

首先可以用邻接矩阵储存有向图,(若为非连通图再加判断条件即可),用一个二维数组如d[i][j]表示i到j的最短路径的长度距离,初始时先将邻接矩阵中能直接相邻的两个节点的距离权值赋值给d,接下来就要找出不是直接到达但是距离反而比直接到达的距离减少了的情况,对于每两个点来说,中间可能经过从1到n-2个点不等,算法实现是一个三重for循环,其中k为经过的中转点,i为起点,j为终点,通过比较d[i][j]和d[i][k]+d[k][j]的大小来更新d[i][j]的最小值,初始时若d[i][j]为无穷大(即没有i->j),在这三重for循环下,上一次循环带来的更新,可以为下一个做出贡献,本质上是对于d的二维矩阵的不断更新,d[i][j]同时也可以代表i到j中间经过了很多个节点,这些都可能在d[I][k]+d[k][j]被包含了,也就是说,从i到j中间经过一个点的情况,当下次循环选到第二个点时,第一次选到的点已经包含在上次的d中了,也就是说越往后遍历,可能从i到j经过的点越多,虽然从表面上看中转点一次只有一个k,但是其中可能有若干个点已经压缩包含在最短路径里了,这样遍历到最后,也就找到了从任一点i到j的最短距离和经过的路径。

关于本题:

在理解Floyd算法的前提下,本题其实并不难,只是有些细节需要考虑,比如1->22->3如何变成1->2->3,笔者治理有限用的是最笨的方法(字符串直接变换)。

#include <iostream>
#include <cstring>
#include <algorithm>
#include<vector>
#include <iomanip>
#include <string>
#define maxm 105
#define maxn 9999
using namespace std;

int G[maxm][maxm];
int n,m;
int d[maxm][maxm];
string path[maxm][maxm];
void Floyd(int d[maxm][maxm],string path[maxm][maxm])
{
    for(int i=0; i<n; i++)
        for(int j=0; j<n; j++)
        {
            d[i][j]=G[i][j];
            if(d[i][j]!=maxn)
            {
                path[i][j]=to_string(i)+"->"+to_string(j);
                //cout<<path[i][j]<<" ";
            }
        }
    for(int k=0; k<n; k++)//中转点,虽然一次只遍历一个,实则i->j在经过k之前可能已经更新了最小值(经过了若干节点)
        for(int i=0; i<n; i++)
            for(int j=0; j<n; j++)
            {
                if(d[i][k]+d[k][j]<d[i][j])
                {
                    d[i][j]=d[i][k]+d[k][j];
                    path[i][j]=path[i][k]+path[k][j];
                }
            }

}
string func(string s)
{
    int len=0;
    for(int i=0; i<s.length()-1; i++)
    {
        if(s[i]==s[i+1])
        {
            len=len+1;
            for(int j=i+2; j<s.length(); j++)
            {
                s[j-1]=s[j];
            }

        }
    }
    /*
    cout<<len<<" ";
    cout<<s<<" ";
    */
    len/=2;//由于字符串长度没有变,会导致len的值大一倍,这个地方要理解
    string ss="";
    for(int i=0; i<s.length()-len;i++)
        ss+=s[i];
    return ss;

}
int main()
{
    cin>>n>>m;
    memset(G,maxn,sizeof(G));
    for(int i=0; i<n; i++)
        for(int j=0; j<n; j++)
            if(i==j)
                G[i][j]=0;
    memset(d,maxn,sizeof(d));
    //memset(path,'-1',sizeof(path));
    for(int i=0; i<m; i++)
    {
        int s,d,v;
        cin>>s>>d>>v;
        G[s][d]=v;
    }
    Floyd(d,path);
    /*
    cout<<endl;
    for(int i=0; i<n; i++)
    {

        for(int j=0; j<n; j++)
            cout<<d[i][j]<<" ";
        cout<<endl;
    }
    */
    for(int i=0; i<2; i++)
    {
        int ss,dd;
        cin>>ss>>dd;
        if(d[ss][dd]>=maxm)
        {
            cout<<ss<<"->"<<dd<<":"<<"-1"<<endl;
            continue;
        }
        //cout<<path[ss][dd]<<" ";
        string a=func(path[ss][dd]);
        cout<<a<<":"<<d[ss][dd]<<endl;
    }
    int idxx=-1,idxy=-1;
    int minm=0;
    for(int i=0; i<n; i++)
        for(int j=0; j<n; j++)
            if(d[i][j]>minm&&d[i][j]<maxn)
            {
                minm=d[i][j];
                idxx=i;
                idxy=j;
            }
    //cout<<path[idxx][idxy]<<" ";
    string a=func(path[idxx][idxy]);
    cout<<a<<":"<<d[idxx][idxy];
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值