Problem Description
给你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)
(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
解题思路:解题关键先保证是最短距离,其次如果距离相等的话,再保证最小费用。首先要预处理一下,因为读入数据可能出现重边,如果不处理就会进行覆盖原来的权值,这样就出错了。
AC代码一Dijkstra:
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int INF = 0x3f3f3f3f; 4 const int MAXN = 1005; 5 int n,m,a,b,d,p,s,t,dis[MAXN],cis[MAXN],cost[MAXN][MAXN],G[MAXN][MAXN];//dis数组记录当前节点离起点的最短路径,cis数组记录当前节点离起点的最小费用,cost数组和G数组分别记录费用和节点之间的关系 6 bool vis[MAXN]; 7 void Dijkstra(){ 8 for(int i=1;i<=n;++i){//先默认初始化dis和cis为起点到各节点的最短路径和最小费用 9 dis[i]=G[s][i]; 10 cis[i]=cost[s][i]; 11 } 12 dis[s]=cis[s]=0;vis[s]=true;//到自己的距离为0,且0花费 13 for(int i=1;i<n;++i){ //剩下遍历n-1个节点 14 int k=-1; //先标记为-1 15 for(int j=1;j<=n;++j)//查找dis中的最小权值 16 if(!vis[j] && (k==-1 ||dis[j]<dis[k]))k=j;//找到最小权值的下标 17 if(k==-1)break; //说明已经全部归纳了,直接退出当前循环,否则才可以进行下面的松弛操作 18 vis[k]=true; //该点已经归纳最短路径的集合 19 for(int j=1;j<=n;++j){//更新起点到每个节点的最短路径 20 if(!vis[j]){ //如果还没有归纳进去 21 if(dis[j]>dis[k]+G[k][j]){ 22 cis[j]=cis[k]+cost[k][j]; 23 dis[j]=dis[k]+G[k][j]; 24 } 25 else if(dis[j]==dis[k]+G[k][j] && cis[j]>cis[k]+cost[k][j]) //如果当前多条最短路径,则取两者中较小费用 26 cis[j]=cis[k]+cost[k][j]; 27 } 28 } 29 } 30 } 31 int main() 32 { 33 while(~scanf("%d %d",&n,&m) && (m+n)){ 34 for(int i=1;i<=n;++i){ 35 for(int j=1;j<=n;++j) 36 if(i==j)G[i][j]=cost[i][j]=0;//到自身的距离和费用都为0 37 else G[i][j]=cost[i][j]=INF;//其余初始化为INF 38 } 39 memset(vis,false,sizeof(vis)); 40 for(int i=1;i<=m;++i){ 41 scanf("%d %d %d %d",&a,&b,&d,&p); 42 if(G[a][b]>d){//预处理,考虑到重边的情况 43 G[a][b]=G[b][a]=d; 44 cost[a][b]=cost[b][a]=p; 45 } 46 else if(G[a][b]==d && cost[a][b]>p)//如果a到b的距离等于原来的话,取最小费用 47 cost[a][b]=cost[b][a]=p; 48 } 49 scanf("%d %d",&s,&t); 50 Dijkstra(); 51 printf("%d %d ",dis[t],cis[t]); 52 } 53 return 0; 54 }
AC代码二(优先队列默认最大堆实现Dijkstra迪杰斯特拉算法):
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int INF = 0x3f3f3f3f; 4 const int MAXN = 1005; 5 int n,m,a,b,d,p,s,t,dis[MAXN],cis[MAXN],cost[MAXN][MAXN],G[MAXN][MAXN];//dis数组记录当前节点离起点的最短路径,cis数组记录当前节点离起点的最小费用,cost数组和G数组分别记录费用和节点之间的关系 6 void Dijkstra(){ 7 priority_queue< pair<int,int> > que;//最大堆优先队列 8 dis[s]=cis[s]=0;//到自己的距离为0,且0花费 9 que.push(make_pair(-dis[s],s));//加上负号实现最大堆,便于取出最短路径 10 while(!que.empty()){ 11 int k=que.top().second;//每次取出队首元素即最短路径 12 que.pop();//弹出队首元素 13 for(int i=1;i<=n;++i){ //更新邻边权值最小的邻接点 14 if(dis[i]>dis[k]+G[k][i]){ 15 cis[i]=cis[k]+cost[k][i]; 16 dis[i]=dis[k]+G[k][i]; 17 que.push(make_pair(-dis[i],i)); //如果有最小权值的邻接点,加入队列中去,记得加负号,因为这是最大堆 18 } 19 else if(dis[i]==dis[k]+G[k][i] && cis[i]>cis[k]+cost[k][i]) //如果当前多条最短路径,则取两者中较小费用 20 cis[i]=cis[k]+cost[k][i]; 21 } 22 } 23 } 24 int main() 25 { 26 while(~scanf("%d %d",&n,&m) && (m+n)){ 27 for(int i=1;i<=n;++i) 28 dis[i]=cis[i]=INF;//全部初始化为INF无穷大 29 for(int i=1;i<=n;++i){ 30 for(int j=1;j<=n;++j) 31 if(i==j)G[i][j]=cost[i][j]=0;//到自身的距离和费用都为0 32 else G[i][j]=cost[i][j]=INF;//其余初始化为INF 33 } 34 for(int i=1;i<=m;++i){ 35 scanf("%d %d %d %d",&a,&b,&d,&p); 36 if(G[a][b]>d){//预处理,考虑到重边的情况 37 G[a][b]=G[b][a]=d; 38 cost[a][b]=cost[b][a]=p; 39 } 40 else if(G[a][b]==d && cost[a][b]>p)//如果a到b的距离等于原来的话,取最小费用 41 cost[a][b]=cost[b][a]=p; 42 } 43 scanf("%d %d",&s,&t); 44 Dijkstra(); 45 printf("%d %d ",dis[t],cis[t]); 46 } 47 return 0; 48 }