给你n个点,m条无向边,每条边都有长度d和花费p,给你起点s终点t,要求输出起点到终点的最短距离及其花费,如果最短距离有多条路线,则输出花费最少的。
Input
输入n,m,点的编号是1~n,然后是m行,每行4个数 a,b,d,p,表示a和b之间有一条边,且其长度为d,花费为p。最后一行是两个数 s,t;起点s,终点。n和m为0时输入结束。
(1< n< =1000, 0< m< 100000, s != t)
Output
输出 一行有两个数, 最短距离及其花费。
Sample Input
3 2
1 2 5 6
2 3 4 5
1 3
0 0
Sample Output
9 11
分析与解答
这题我调试了八个小时
我总结一下我对dijkstra的认识
1.dijkstra算法可以求从单个源点出发到所有结点的最短路,这个题就是坑到这了,我写两个参数就wrong answer了,就是说,你调用这个函数只需要一个参数,就是起点。终点是n已经固定了,现在你说终点是t,哪怕走到终点n的路不经过t,你输出dis[t],也是从起点到t的最短路。
每标记一次就说明被标记的这个数的dis已经确定了。我们循环n次目的就是为了标记n次确定n个数的dis。我们初始化起点的dis是0其他的是inf,我们循环n次,第一个确定下来的就是起点。然后标记,注意这个标记是在那n次循环里的。
2.这个题第二个坑就是,两个点之间的路有多条,如果路的距离相同时,还要找花费最小的
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define INF 100100
using namespace std;int vis[1100];int dis[1100];
int map[1100][1100];
int value[1100][1100];
int v[1100];
int n;
void dijkstra(int s){memset(vis,0,sizeof(vis));for(int i=1;i<=n;++i){dis[i]=INF;v[i]=INF;}dis[s]=v[s]=0;for(int i=1;i<=n;++i){//循环n次,每一次都选一个点标记上int x,m=INF;for(int y=1;y<=n;++y){//所有未标号节点中选dis最小的结点if(!vis[y]&&dis[y]<=m)m=dis[x=y];}vis[x]=1;for(int y=1;y<=n;++y)//从x出发的所有边(x,y),更新dis[y]{ if (dis[y] > dis[x] + map[x][y]){dis[y]= dis[x]+map[x][y];v[y] = v[x] + value[x][y];}else if (dis[y] == dis[x] + map[x][y] && v[y] > v[x] + value[x][y]){v[y] = v[x] + value[x][y];}}}
}int main() {int m;int a,b,c,p;int s,t;while(cin>>n>>m){if(m==0&&n==0) return 0;//// memset(v,INF,sizeof(v));memset(map,INF,sizeof(map));memset(value,INF,sizeof(value));for(int i=0;i<m;++i){scanf("%d%d%d%d",&a,&b,&c,&p);if(c<map[a][b]){map[a][b]=map[b][a]=c;value[a][b]=value[b][a]=p;}if(c==map[a][b]&&value[a][b]>p){value[b][a]=value[a][b]=p;}}scanf("%d%d",&s,&t);dijkstra(s);printf("%d %d\n",dis[t],v[t]);}
}