鱼C论坛

 找回密码
 立即注册
查看: 2650|回复: 2

[技术交流] 027:最短路

[复制链接]
发表于 2018-2-13 15:01:38 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 Messj 于 2018-6-8 23:41 编辑

Problem Description
在每年的校赛里,所有进入决赛的同学都会获得一件很漂亮的t-shirt。但是每当我们的工作人员把上百件的衣服从商店运回到赛场的时候,却是非常累的!所以现在他们想要寻找最短的从商店到赛场的路线,你可以帮助他们吗?



Input
输入包括多组数据。每组数据第一行是两个整数N、M(N<=100,M<=10000),N表示成都的大街上有几个路口,标号为1的路口是商店所在地,标号为N的路口是赛场所在地,M则表示在成都有几条路。N=M=0表示输入结束。接下来M行,每行包括3个整数A,B,C(1<=A,B<=N,1<=C<=1000),表示在路口A与路口B之间有一条路,我们的工作人员需要C分钟的时间走过这条路。
输入保证至少存在1条商店到赛场的路线。


Output
对于每组输入,输出一行,表示工作人员从商店走到赛场的最短时间


Sample Input
2 1 1 2 3 3 3 1 2 5 2 3 5 3 1 2 0 0


Sample Output
3 2

本帖被以下淘专辑推荐:

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2018-2-13 15:03:24 | 显示全部楼层
Floyd-Warshall算法(Floyd-Warshall algorithm)是解决任意两点间的最短路径的一种算法,可以正确处理有向图或负权的最短路径问题,同时也被用于计算有向图的传递闭包。Floyd-Warshall算法的时间复杂度为O(N3),空间复杂度为O(N2)。

1)算法思想原理:

     Floyd算法是一个经典的动态规划算法。用通俗的语言来描述的话,首先我们的目标是寻找从点i到点j的最短路径。从动态规划的角度看问题,我们需要为这个目标重新做一个诠释(这个诠释正是动态规划最富创造力的精华所在)

      从任意节点i到任意节点j的最短路径不外乎2种可能,1是直接从i到j,2是从i经过若干个节点k到j。所以,我们假设Dis(i,j)为节点u到节点v的最短路径的距离,对于每一个节点k,我们检查Dis(i,k) + Dis(k,j) < Dis(i,j)是否成立,如果成立,证明从i到k再到j的路径比i直接到j的路径短,我们便设置Dis(i,j) = Dis(i,k) + Dis(k,j),这样一来,当我们遍历完所有节点k,Dis(i,j)中记录的便是i到j的最短路径的距离。

2).算法描述:

a.从任意一条单边路径开始。所有两点之间的距离是边的权,如果两点之间没有边相连,则权为无穷大。   

b.对于每一对顶点 u 和 v,看看是否存在一个顶点 w 使得从 u 到 w 再到 v 比己知的路径更短。如果是更新它。
#include<cstdio>

using namespace std;

int ans[101][101];
int main()
{
        int n,m;
        while(scanf("%d%d",&n,&m)!=EOF)
        {
                if(n==0&&m==0)        break;
                for(int i=1;i<=n;i++)
                {
                        for(int j=1;j<=n;j++)
                                ans[i][j]=-1; 
                        ans[i][i]=0;
                }
                while(m--)
                {
                        int a,b,c;
                        scanf("%d%d%d",&a,&b,&c);
                        ans[a][b]=ans[b][a]=c;
                }
                for(int k=1;k<=n;k++)
                        for(int i=1;i<=n;i++)
                                for(int j=1;j<=n;j++)
                                {
                                        if(ans[i][k]==-1||ans[k][j]==-1)
                                                continue;
                                        if(ans[i][j]==-1||ans[i][j]>(ans[i][k]+ans[k][j]))
                                                ans[i][j]=ans[i][k]+ans[k][j];
                                }
                printf("%d\n",ans[1][n]);
        }
        return 0;
}
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-2-13 15:48:27 | 显示全部楼层
本帖最后由 Messj 于 2018-2-13 15:51 编辑

推荐 Dijkstra最短路径算法 文章 http://blog.csdn.net/qq_35644234/article/details/60870719

1、最短路径问题介绍

问题解释:
从图中的某个顶点出发到达另外一个顶点的所经过的边的权重和最小的一条路径,称为最短路径

解决问题的算法:

迪杰斯特拉算法(Dijkstra算法)
弗洛伊德算法(Floyd算法)
SPFA算法
这篇博客,我们就对Dijkstra算法来做一个详细的介绍

2、Dijkstra算法介绍

算法特点:

迪科斯彻算法使用了广度优先搜索解决赋权有向图或者无向图的单源最短路径问题,算法最终得到一个最短路径树。该算法常用于路由算法或者作为其他图算法的一个子模块。

算法的思路

Dijkstra算法采用的是一种贪心的策略。
声明一个数组dis来保存源点到各个顶点的最短距离和一个保存已经找到了最短路径的顶点的集合:T,初始时,原点 s 的路径权重被赋为 0(dis[ s ]=0)。若对于顶点 s 存在能直接到达的边(s,m),则把dis[m]设为w(s, m),同时把所有其他(s不能直接到达的)顶点的路径长度设为无穷大。初始时,集合T只有顶点s。
然后,从dis数组选择最小值,则该值就是源点s到该值对应的顶点的最短路径,并且把该点加入到T中,OK,此时完成一个顶点,
然后,我们需要看看新加入的顶点是否可以到达其他顶点并且看看通过该顶点到达其他点的路径长度是否比源点直接到达短,如果是,那么就替换这些顶点在dis中的值。
然后,又从dis中找出最小值,重复上述动作,直到T中包含了图的所有顶点。
#include<cstdio>
#include<vector>
#define N 101

using namespace std;

struct E
{
        int next;
        int c;
};
vector<E> edge[N];
bool mark[N];
int dis[N];
int main()
{
        int n,m;
        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;
                        scanf("%d%d%d",&a,&b,&c);
                        E tmp;
                        tmp.c=c;
                        tmp.next=b;
                        edge[a].push_back(tmp);
                        tmp.next=a;
                        edge[b].push_back(tmp);
                }
                for(int i=1;i<=n;i++)
                {
                        dis[i]=-1;
                        mark[i]=false;
                }
                dis[1]=0;
                mark[1]=true;        //点1加入集合 
                int newP=1;                //新加入集合的点为点1 
                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;
                                if(mark[t]==true)        continue;
                                if(dis[t]==-1||dis[t]>(dis[newP]+c))
                                        dis[t]=dis[newP]+c;
                        }
                        int min=123123123;
                        for(int j=1;j<=n;j++)
                        {
                                if(mark[j])        continue;
                                if(dis[j]==-1)        continue;
                                if(dis[j]<min)
                                {
                                        min=dis[j];
                                        newP=j;
                                }
                        }
                        mark[newP]=true; 
                }
                printf("%d\n",dis[n]);
        }
        return 0;
}
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-11-6 09:52

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表