题目描述:
给你n个点,m条无向边,每条边都有长度d和花费p,给你起点s终点t,要求输出起点到终点的最短距离及其花费,如果最短距离有多条路线,则输出花费最少的。
输入:
输入n,m,点的编号是1~n,然后是m行,每行4个数 a,b,d,p,表示a和b之间有一条边,且其长度为d,花费为p。最后一行是两个数 s,t;起点s,终点t。n和m为0时输入结束。
(1< n<=1000, 0< m< 100000, s != t)
输出:
输出 一行有两个数, 最短距离及其花费。
样例输入:
3 2
1 2 5 6
2 3 4 5
1 3
0 0
样例输出:
9 11
来源:
2010年浙江大学计算机及软件工程研究生机试真题
太坑了,被不细心坑了一晚上
分析:
这是一道求单源最短路径的题,显然要使用dijkstra算法。有一点要注意的是:
1.以中间点更新最短路线时,如果距离相同,要选择最少花费的路线
2.选择新的中间点加入已经求得最小路径的点的集合时,如果距离相同,要选择最少花费的点 ( 其实这条可以忽略,因为当有若干个点的距离同时相等时,不管他们的花费如何,接下来他们都会依次地被加入,因为很容易证明,随后的更新不可能再找到比这个距离更小的路径了)
提交了好几次有RE也有WA。RE是因为题目的数据范围我没看清,导致数组开太小了。后来一直WA,一直找不到原因,看了其他人的代码发现上述注意事项里的第二条,添加了之后还是不对。近乎抓狂。。。最后发现是忘记初始化mark数组了。。。。。。。
附上源码:
#include <stdio.h>
#include <vector>
using namespace std;
#define MAXN 1001
#define MAXM 100001
#define INF 0x7fffffff
int n,m,s,t;
bool mark[MAXN];
int dis[MAXN];
int price[MAXN];
struct Edge{
int next;
int d;
int p;
};
vector<Edge> edge[MAXM]; //邻接链表
void dijkstra(int s, int t){
int currentPoint = s; //设置起点为当前中间点
mark[s] = true; //标记起点已经找到最短路径
dis[s] = 0; //起点到起点的最短路径为0
price[s] = 0; //起点到起点的花费为0
int tmp;
while(m--){
for(int i = 0; i<edge[currentPoint].size();i++){ //访问当前中间点的所有边
tmp = edge[currentPoint][i].next; //取得该边另一头的节点
if(mark[tmp]){ //如果已经找到最短路径了,舍弃
continue;
}else{
if ( dis[tmp] > (dis[currentPoint] + edge[currentPoint][i].d) ){
//如果当前到达该节点的最小距离大于通过中间点currentPoint到达该点的距离,则更新
dis[tmp] = dis[currentPoint] + edge[currentPoint][i].d;
price[tmp] = price[currentPoint] + edge[currentPoint][i].p;
}else if(dis[tmp] == (dis[currentPoint] + edge[currentPoint][i].d) &&
price[tmp] > price[currentPoint] + edge[currentPoint][i].p){
//如果距离相同,再判断是否要更新最小花费
price[tmp] = price[currentPoint] + edge[currentPoint][i].p;
}
}
}
//更新完该中间点的所有相邻的点后,在当前所有未找到最短路径的点中找到一个路径最小的点,
//设置为新的当前中间点,并标记它找到了最短路径
int tmpPoint = -1;
int tmpMin = INF;
for(int j=1; j<=n;j++){
if(!mark[j]){
if( dis[j] != tmpMin){
if(dis[j] < tmpMin){
tmpMin = dis[j];
tmpPoint = j;
}
}else if(price[tmpPoint] > price[j])
tmpPoint = j;
}
}
currentPoint = tmpPoint;
mark[currentPoint] = true;
}
}
int main(){
int a,b,d,p;
Edge tmp;
while( scanf("%d%d", &n,&m) != EOF){
if(m==0 && n==0)
break;
for( int j=1; j<=n; j++){
edge[j].clear(); //初始化邻接链表
dis[j] = INF; //初始化距离和花费
price[j] = INF;
mark[j] = false; //初始化mark,每个点都没访问过 (太坑啦!)
}
for( int i=0; i< m; i++){
scanf("%d%d%d%d", &a,&b,&d,&p);
//无向图要存储两个点的邻接链表
tmp.next = b;
tmp.d = d;
tmp.p = p;
edge[a].push_back(tmp);
tmp.next = a;
edge[b].push_back(tmp);
}
scanf("%d%d", &s,&t);
dijkstra(s,t);
printf("%d %d\n", dis[t], price[t]);
}
return 0;
}
总结:
1.一定要细心啊
2.debug能力有待提高
3.对vector的使用和dijkstra算法有了一定的了解
睡觉
本文通过一道OJ题目介绍了使用Dijkstra算法解决最短路径问题,强调了在实现过程中需要注意的细节,包括当距离相同时优先选择花费较少的路径。作者分享了在解题过程中的错误和教训,如数组大小设置、初始化标记数组的重要性以及对vector和Dijkstra算法的理解加深。
451

被折叠的 条评论
为什么被折叠?



