-
题目描述:
-
在每年的校赛里,所有进入决赛的同学都会获得一件很漂亮的t-shirt。但是每当我们的工作人员把上百件的衣服从商店运回到赛场的时候,却是非常累的!所以现在他们想要寻找最短的从商店到赛场的路线,你可以帮助他们吗?
-
输入:
-
输入包括多组数据。每组数据第一行是两个整数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条商店到赛场的路线。
当输入为两个0时,输入结束。
-
输出:
-
对于每组输入,输出一行,表示工作人员从商店走到赛场的最短时间。
-
样例输入:
-
2 1 1 2 3 3 3 1 2 5 2 3 5 3 1 2 0 0
-
样例输出:
-
3 2
/**************************************************************
Problem: 1447
User: hrdjmax2
Language: C
Result: Accepted
Time:20 ms
Memory:952 kb
****************************************************************/
-
Floyd算法
-
对点数量敏感,算法时间复杂度是O(N^3),所以如果点超过200个的时候,效率可能就太差了
-
另外邻接矩阵的方案比较适合floyd算法,如果原图不是邻接矩阵,我们可以将其转换成邻接矩阵
-
当Floyd算法执行完后,图中所有的节点对的最短距离都已经存放在邻接矩阵里面了
-
所以也可以解决全源最短路径问题 算法思想比较简单,注释写的比较详细
#include<stdio.h> int Re_road[101][101];//记录从i-j的最小通路权值; int main() { int N,M,i,j,k; int f,s,v; while(scanf("%d %d",&N, &M)!=EOF) { if(0 == N && 0 == M) break; for(i =1; i<=N; i++) { for(j =1; j<=N; j++) { Re_road[i][j] = -1;//初始化时默认每个节点之间距离是无穷的:(-1) } Re_road[i][i] = 0;//自己的权值是:0 } while(M--)//M条道路输入 { scanf("%d %d %d",&f, &s, &v);//输入第一个节点f第二个节点s v代表道路权值 Re_road[f][s] = Re_road[s][f] = v;//道路的权值是双向的,且两个节点间直接的道路应该只有一条 } //动态规划Re_road[x][y]记录从x-y的最小通路权值 for(k = 1; k <= N; k++)//标示两个节点间可能经过的(1~k)个中间节点 { for(i= 1; i <= N; i++) { for(j = 1; j<=N; j++) { if(-1 == Re_road[i][k] || -1 == Re_road[k][j])//i~j之间经过K节点的话,无通路! { continue;//则这种情况下不可通过K } //如果程序能跑到这里,说明Re_road[i][k]和Re_road[k][j]都是通的; if(-1 == Re_road[i][j] || Re_road[i][j] > Re_road[i][k]+Re_road[k][j]) {//如果i-j(不一定是直接的因为之前的操作在插入中间节点后权值可能有更新过)之间没有通路又或者,i-j之间的直接通路不如i-(1..k....)-j的权值小的话更新ij之间的权值 Re_road[i][j] = Re_road[i][k]+Re_road[k][j];//更新了通路权值 } } } } printf("%d\n", Re_road[1][N]);//求出从商店(1)到赛场(N)的最小距离 } }
Dijstral算法解法:
-
Dijstral算法比Floyd难懂不少,现在还不能说完全吃透
-
它能解决单源最短路径问题,效率比Floyd好些
-
Dis[i]存放 1节点->i节点的暂时最短距离(结束算法时候,则得到最终的最短距离)
mark[i]标记这个点有没有放在最短距离集合 P中(P只是一个抽象的概念)
vector<E>Edge[101]//数据结构模拟临接链表 strcut E{ int next; int value}
初始化 Dis[i] = -1(表示无穷) 1<i<=n; Dis[1] = 0自己和自己的距离是0
mark[i] = false (表示不在P中) 1<i<=n mark[1] = true一开始1就被放进集合P中
-
NewP 存放 最近被放入集合P的节点
-
-
用上图示例说明算法思想:(水平有限)
-
一开始P集合只有1,遍历与1有直接通路的节点(t1 t2 ...tn):
-
如果那个节点已经在P中,说明最短距离已经被确定那么就continue跳过;
-
Dis[t] == -1说明还没有记录通路权值在Dis中,而t是节点1的临接节点,所以一定是有通路的,那么Dis[t] = Dis[Newp=1]+c(1->t的距离);
-
Dis[t] > Dis[NewP]+c 则说明1直接去节点t的距离 比 1->NewP->t的距离要大,所以更新之;
-
之后在已经得到的最短路径中选取不在P中的最短的那个节点作为NewP,加入P集合中,重复上面的操作
-
#include <iostream> #include <cstdio> #include <vector> using namespace std; struct E { int next;//临接的点 int value;//边权值 }; vector<E> Edge[101];//链接链表 int Dis[101];//记录1->节点1-100的最短距离 bool mark[101];//标记在不在最短距离集合P(Newp)里面 int main() { int n,m,i,j,a,b,v; while(cin>>n>>m) { if(0 == n && 0 == m) { break;//结果 } for(i=1; i<=n; i++) Edge[i].clear();//邻接链表初始化 Dis[1] = 0;//1-1 距离为0 mark[1] = true;//1在最短距离集合P中 for(i = 2; i<=n; i++) { Dis[i] = -1;//一开始所有的点都是无穷远 mark[i] = false;//一开始除了1之外的节点都不在最短距离集合P中 } while(m--) { cin>>a>>b>>v; E tmp; tmp.next = a; tmp.value = v; Edge[b].push_back(tmp);//b->a 存入临接链表 tmp.next = b; Edge[a].push_back(tmp);//a->b 存入临接链表(注意这个是双向的) } int NewP = 1;//新加入的最短距离集合的点; for(i= 1; i<n; i++) { for(j = 0; j<Edge[NewP].size(); j++) { int t = Edge[NewP][j].next;//临接的点 int c = Edge[NewP][j].value; if(true == mark[t]) continue; if(-1 == Dis[t] || Dis[t] > Dis[NewP]+c)//当点t与1节点没有直接相连时或者距离超过1-newp点-t节点的距离时候 { Dis[t] = Dis[NewP]+c; } } int min = 999999999; for(int j = 1; j <=n ; j++) { if(true == mark[j]) continue;//已经在最短距离集合P中的话,不参与挑选; if(-1 == Dis[j]) continue;//到达不了的节点不参与挑选; if(Dis[j] < min) { min = Dis[j]; NewP = j; } } mark[NewP] = true;//选取距离最短的节点加入P中 } printf("%d\n", Dis[n]); } }