<题目链接>
题目描述
给定一个 N 个点, M 条有向边的带非负权图,请你计算从 S 出发,到每个点的距离。
数据保证你能从 S 出发到任意点。
输入格式:
第一行为三个正整数 N,M,S 。 第二行起 M 行,每行三个非负整数 ui, vi, wi,表示从 ui到 vi 有一条权值为 wi 的边。
输出格式:
输出一行 N 个空格分隔的非负整数,表示 S 到每个点的距离。
1<=N<=100000
1<=M<=200000
解题分析:
由于n和m的数据太大,所以这里不能够用普通的dijkstra算法,因为它的复杂度为O(n^2),所以我们这里要用的是复杂度为O(nlog(n))的加上堆优化的dijkstra算法,并且,下面的代码存图用的是链式前向星。
#include <bits/stdc++.h> using namespace std; const int M = 2e5 + 7; const int ME = 2e5 + 7; const int INF = 2147483647; int head[M]; int cnt, dis[M], n, m, vis[M]; struct EDGE { int next; //上一条同起点的边在edge[]数组中的储存坐标 int to; //该边的终点 int w; //该边的权值 }edge[ME]; void addedge(int u, int to, int w) { //链式前向星的构建 edge[++cnt].to = to; edge[cnt].w = w; edge[cnt].next = head[u]; //当前边的下一条边是输入的以u为顶点的上一条边 (next就相当于) head[u] = cnt; //表示以u为起点的当前边在edge[]数组中的序号 } struct node { int dis, index; bool operator < (const node &k) const { //重载排序 return dis>k.dis; //由于要保证dis小的优先,所以将dis从大到小排序 } }d[M]; void dijkstra(int s) { for (int i = 1; i <= n; i++) vis[i] = 0, d[i].dis = INF, d[i].index = i; priority_queue<node> q; d[s].dis = 0; q.push(d[s]); while (!q.empty()) { int u = q.top().index; //队列里此时距离起点最短的点 q.pop(); if (vis[u]) continue; vis[u] = 1; for (int i = head[u]; ~i; i = edge[i].next) { //链式前向星,以u为起点的所有线段的遍历方法(模板) int v = edge[i].to; if (d[v].dis>d[u].dis + edge[i].w) { //更新以 u 为起点的,所有与它相连的线段的终点到s起点的最短距离 d[v].dis = d[u].dis + edge[i].w; q.push(d[v]); //由于更新了d[v].dis,所以所有以d[v].index为起点的线段也要更新,所以将d[v]压入队列 } } } return; } int main() { int s; cnt = 0; memset(head, -1, sizeof(head)); scanf("%d%d%d", &n, &m, &s); for (int i = 1; i <= m; i++) { int u, to, w; scanf("%d%d%d", &u, &to, &w); addedge(u, to, w); } dijkstra(s); //以s为起点 for (int i = 1; i <= n; i++) //学习这种代码风格 printf("%d%s", d[i].dis, i == n ? " \n" : " "); return 0; }
2018-08-13