codeforces C. Dijkstra? (dijkstra算法)

题目描述

You are given a weighted undirected graph. The vertices are enumerated from 1 to n. Your task is to find the shortest path between the vertex 1 and the vertex n.

Input
The first line contains two integers n and m (2 ≤ n ≤ 105, 0 ≤ m ≤ 105), where n is the number of vertices and m is the number of edges. Following m lines contain one edge each in form ai, bi and wi (1 ≤ ai, bi ≤ n, 1 ≤ wi ≤ 106), where ai, bi are edge endpoints and wi is the length of the edge.

It is possible that the graph has loops and multiple edges between pair of vertices.

Output
Write the only integer -1 in case of no path. Write the shortest path in opposite case. If there are many solutions, print any of them.

Examples
input

5 6
1 2 2
2 5 5
2 3 4
1 4 1
4 3 3
3 5 1

output

1 4 3 5

input

5 6
1 2 2
2 5 5
2 3 4
1 4 1
4 3 3
3 5 1

output

1 4 3 5

题目大意

给你一个带权无向图。顶点从1到n进行编号。你的任务是找到顶点1和顶点n之间的最短路径,并输出最短路路径,如果有许多解决方案,打印其中任意一个。如果不存在最短路,就等于-1。

dijkstra算法求最短路并记录路径

     dijkstra就不说了,通常题目都只会用dijkstra求一个最短路数值,而此处要记录最短路路径。下面就说怎么记录路径:
1.用dijkstra算法求最短路径,松弛操作其实就是一个不断选择最短路路径的过程 ,每次松弛都是一个“选择比当前路径更短的路径”的过程,最终所有松弛操作结束就意味着最短路路径的选择确定了。
2.因为路径的选择存在于松弛操作中,所以我们就应该在松弛操作中来记录路径。在dijkstra算法中,每次松弛操作都是用一个“已经确定了最短路的点”来松弛其它点,即每次用来松弛的边都是由“已经确定了最短路的点”指向“最短路有待确认”的点,所以可以用pre[v]来记录v点在最短路径上的上一个点。用下列语句不断松弛确认点v的最短路以及用pre[v]记录v的上一个点:

 if(dist[v]>dist[u]+w){
            dist[v]=dist[u]+w;
            pre[v]=u;   //记录v的上一个点
        }

这样每个点都记录了其在最短路径上的上一个点,那么就记录一条从终点到起点的路径。只需要将其反向输出即可。

那么简单的一个过程居然被我写那么啰嗦

C++代码

//C++  dijkstra算法找最短路径代码

#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
typedef long long LL;
const int N=1e5+100,M=2e5+100;
const LL inf=1e16;
struct edge{
int u;
int v;
LL w;
int next;
}e[M];
int head[N],cnt=0;
void Insert(int u,int v,LL w){
cnt++;
e[cnt].u=u;
e[cnt].v=v;
e[cnt].w=w;
e[cnt].next=head[u];
head[u]=cnt;
}

struct node{
int u;
LL dis;
bool operator < (const node& a) const{
return dis>a.dis;
}
};
int n,m,vis[N],pre[M];
LL dist[N];
void dijkstra(int start){
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=n;i++) dist[i]=inf;
    priority_queue<node>q;
    dist[start]=0;
    q.push((node){start,0});
    node temp;
 while(!q.empty()){
    temp=q.top();
    q.pop();
    int u=temp.u;
    if(vis[u]) continue;
    vis[u]=1;
    for(int i=head[u];i>=0;i=e[i].next){
            int v=e[i].v;
            LL w=e[i].w;
        if(dist[v]>dist[u]+w){
            dist[v]=dist[u]+w;
            q.push((node){v,dist[v]});
            pre[v]=u;
        }

    }
}


}

//输出路径:将pre[]记录的路径逆序输出
void dfs(int u){
if(u==1) {
    printf("%d ",u);  //搜到起点时输出起点并回退
    return;
}
dfs(pre[u]);   //搜索u的前一个点
 printf("%d ",u);     //递归回退时输出u
}


int main(){
    memset(head,-1,sizeof(head));
    memset(pre,-1,sizeof(pre));
scanf("%d%d",&n,&m);
int u,v,w;
for(int i=1;i<=m;i++){
    scanf("%d%d%d",&u,&v,&w);
    Insert(u,v,w);
    Insert(v,u,w);
}
dijkstra(1);
if(dist[n]==inf) printf("-1");   //不存在路径则输出-1
else{
    dfs(n);     //输出最短路 路径
}

return 0;
}
  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值