洛谷某题题目描述
如题,给出一个有向图,请输出从某一点出发到所有点的最短路径长度。
输入格式
第一行包含三个整数 n,m,s,分别表示点的个数、有向边的个数、出发点的编号。
接下来 m 行每行包含三个整数 u,v,w表示一条 u→v 的,长度为 w 的边。
输出格式
输出一行 n 个整数,第 i 个表示 s 到第 i 个点的最短路径,若不能到达则输出 2^{31}-1。
输入输出样例
输入
4 6 1 1 2 2 2 3 2 2 4 1 1 3 5 3 4 3 1 4 3 输出 0 2 4 3
题目所给的例题如图。需要求出点1到所有点的最短距离。
我们先想一下求点1到某个点的最短距离怎么求,假设我们要求点1到点4,首先我们要想到需要把它们之间所有路径全部求一遍,得到最短的距离。
那我们怎么用程序求他们之间的所有路径呢,那肯定还是遍历所有路径,看看那些是他们之间的路径。那既然要遍历所有路径,那不妨把其中所有点与点1的距离求出来,既方便我们求点1到点4的最短距离(如图可得点1到点4最短距离是点1先到点2,再到点4。我们知道到点4的点有三个,分别是1,2,3。点1直接到点4距离为4,点1通过点2到点4距离为1到2的最短距离加上2到4的距离。点1通过点3到点4的最短距离为点1到点3的最短距离加上3到4的距离。)也可以顺带符合题意求出点1到所有点距离。
思路有了,我们如何存储这个图的关系结构呢?数据结构中告诉了我们如何存储图,其实图也是用我们的链表存储,不过和单链表不同,我们的图节点可能连接很多个节点,不像我们的一般链表,每个节点连接的节点个数都相同。(这里为求方便,我们就简化存储)
节点的定义:
struct code
{ // int num;//节点的编号,如点1,点2 。这里为求简便,以结构体数组存储节点,所以以数组下标表示节点编号
int next[10];//该节点连接的节点编号,这里为求方便用数组存储,且假定不超过10;
int longer[10];//该节点与连接节点的距离
int distance;//该节点与出发点的距离
}tu[10];
在存储完数据后,我们应该怎样找到我们的所有路径呢,要找到所有路径,那我们第一时间想到我们的深度优先搜索,可以帮助我们找到每一条路径。
深度优先搜索简介:顾名思义按照深度先进行的搜索,如例题中从点1开始,先到第一个连接的点点2,再到点2连接的第一个点3,再到点3连接的第一个点4,知道点4没有连接了,这时候看3有没有连接,3也没有连接,再看2还有没有连接,2还接着4,那就再看2到4这条路,2到4看完,再看1连接的3,这种先探索完一条路再进行下一条的方法就是深度优先。
广度优先搜索简介:顾名思义按照广度先进行搜索,如例题中从点1开始,先看点1到2,再看点1到3,再看点1到4,看完了1连接的点,再看1连接的第一个点2所连接的点,2连接3,2连接4.这种按照一层一层的搜索就是广度优先。
首先定义函数名称定为dis,参数和返回值类型,因为本次我们采用全局变量,所以不需要返回值,参数的话我们需要算某个点到其他连接点的这条路线是否是最短路线,那我们需要的参数有这个点的编号num,以及连接点的编号n,
int dis(int num,int n)函数出来了。作用是计算点num的下一个连接点next[n]的到出发点的最短距离
int dis(int num,int n)
{//如果num到下一个点next[n]的距离加上num到出发点的最短距离,比num下一个点next[n]的原来到出发点的最短距离小,那么这个新的路径是更短的路径,这个距离覆盖掉原来的最短距离。
代码:
a=tu[ tu[num].next[n] ].distance;//这里只是为了方便简写所以来一个a,赋值为下一个点的原来的最短距离
if( a==0||a>tu[num].distance+tu[num].longer[n] )//如果下一个点的原最短距离为0或者大于新的距离,那么把新的距离给这个最短距离
tu[ tu[num].next[n] ].distance=tu[num].distance+tu[num].longer[n];
这样就求出了我们的一个点到连接点的联系,但是我们还要求剩下的连接点,那我们可以递归求得。
代码:
//在上述代码求得点num与下一个点next[n]后的联系之后,按照深度原则,要求的下一个应该是next[n]与其第一个连接点的联系
dis(tu[num].next[n],1);
/./假设next[n]后面没有连接点了,我们求完了,那下一步是什么呢,是求num的第n个连接点的下一个连接点next[n+1]。
dis(num,n+1);
同样的递归代码我们需要边界条件:参数有两个,num和n,n的话我们知道是表示num的第几个连接点,而next[0]记录了连接点的数量,所以如果n是next[0]+1的时候,我们知道,没有其他的连接点了,以及到达边界了。而num我们知道这是表示第几个点,那他的边界就是0,或者超过总的点数量。
边界n:
if(n==tu[num].next[0]+1)
return 0;
边界num://这里我们将数据初始化为0,那么最终边界就是某一个点的下一个连接点会是0,没有连接点了。那么边界为0.
if(num==0)
return 0;
代码组合起来:
#include<stdio.h>
struct code
{
int next[11];//数组0号这里作为标记有多少条边使用,
int longer[11];
int distance;
};
struct code tu[11];//为求方便数组0这里放弃不用
int dis(int num,int n)
{
int a;
if(n==tu[num].next[0]+1)
return 0;
if(num==0)
return 0;
a=tu[ tu[num].next[n] ].distance;//这里只是为例方便简写所以来一个a
if( a==0||a>tu[num].distance+tu[num].longer[n] )
tu[ tu[num].next[n] ].distance=tu[num].distance+tu[num].longer[n];
dis(tu[num].next[n],1);
dis(num,n+1);
}
int main()
{
int i,j,n,m,s,u,v,w;
for(i=1;i<11;i++)//这里对数据先初始化,方便我们后面对比是否进行了赋值
{
tu[i].distance=0;
for(j=0;j<11;j++)
{
tu[i].next[j]=0;
tu[i].longer[j]=0;
}
}
scanf("%d %d %d",&n,&m,&s);
for(i=1;i<=m;i++)//存入图的数据
{
scanf("%d %d %d",&u,&v,&w);
tu[u].next[0]++;//存入一条边,边数加1
tu[u].next[tu[u].next[0]]=v;//存入连接边的编号
tu[u].longer[tu[u].next[0]]=w;//存入长度
}
dis(s,1);
for(i=1;i<=n;i++)
if(tu[i].distance!=0)
printf("%d,",tu[i].distance);
else printf("no");
return 0;
}
运行截图: