目录
原理概论
边权无负数时,从未访问的所有点中选一个距源点最短的点,用它来更新别的点,循环操作。相当于三角形两边和大于第三边,边长都是正数时,你从别的点绕到这个最短的点肯定比直接到最短的点距离要大,所以我们可以放心地用这个最短的点更新别的点,这是贪心的思想
故事引入
邻接矩阵版 O(N^2版代码)
模板 (这个注释最全,小白不要跳过,有助于理解)
#include<cstdio>//有向边无负权有环也行时间复杂度N^2 数组从0开始而不是1
#include<cstring>//原理:边权无负数时,从未访问的所有点中选一个距源点最短的点,用它来更新别的点,循环操作。相当于三角形两边和大于第三边,边长都是正数时
using namespace std;//你从别的点绕到这个最短的点肯定比直接到最短的点距离要大,所以我们可以放心地用这个最短的点更新别的点,这是贪心的思想
const int MAX=1e4+10,INF=0x3f3f3f3f;
int g[MAX][MAX];//存储图
bool vis[MAX];//表示这个点的最短路是否确定,也就是他是不是在【寻找源点到其他点的最短距离并用它更新其他的点】这个环节中被访问过的点
int n,m,s;
int dis[MAX];//到源点的最短路
void dijkstra(int s) { //迪杰斯特拉算法 源点为s,寻找各点到源点的最短路
memset(dis,0x3f,sizeof(dis));
dis[s]=0;//源点到源点距离为0
for(int i=0; i<n; i++) { //寻找源点到其他点的最短距离并用它更新其他的点,共n个点,所以寻找n次,外层循环时间复杂度为n
int minn=INF,u=-1;//u使dis[u]最小,minn存放该最小的dis[u]
for(int j=0; j<n; j++) { //找出一个未被访问过的最短路,即dis[]最小 内层循环1时间复杂度n
if(!vis[j] && minn>dis[j]) {
minn=dis[j];
u=j;
}
}
if(u == -1) return;//找不到小于 INF 的 dis[u], 说明剩下的顶点和起点 s 不连通
vis[u]=true;//u已被访问
for(int j=0; j<n; j++) { //逐个更新最短路:如果j未访问且u能到达j且以u为中介点可以使dis[j]更优 内层循环2时间复杂度n 共2n^2
if(!vis[j] && g[u][j]!=INF && dis[j]>dis[u]+g[u][j])
dis[j]=dis[u]+g[u][j];//s->j的路径 == s->u->j
}
}
}
int main() {
return 0;
}
例题
注意,这题很坑,数组从0开始的,所以你的数组要是从1开始记得+1,AC代码不贴了,模板加个输入就OK
优先队列+链式前向星优化版 O(NlogN+NM)
模板
#include<cstdio>
#include<iostream>//数组从1开始
#include<cstring>
#include<queue>
using namespace std;
const int INF=0x3f3f3f3f,N=1e5+10;
struct node {
int to,next,w;
friend bool operator < (const node &f1, const node &f2) {
return f1.w > f2.w;
}
} edge[N];
int n,m,cnt,s,t;
int head[N],dis[N];
bool vis[N];
priority_queue<node> Q;
inline void addedge(int from, int to, int w) {
edge[cnt].to = to;
edge[cnt].next = head[from];
edge[cnt].w = w;
head[from] = cnt++;
}
inline void init() {
cnt = 0;
memset(head, -1, sizeof(head));
memset(edge, 0, sizeof(edge));
memset(dis, 0x3f, sizeof(dis));
}
void dijkstra(int s,int t) { //s为源点 t为终点
node z;
z.to=s,z.next=0,z.w=0;
Q.push(z);
dis[s] = 0;
while (Q.size()) {
z = Q.top();
Q.pop();
if(vis[z.to]) continue;//无向图的话请注释掉 因为这是为了剪枝,而无向图不能这么剪枝
vis[z.to]=true;//无向图的话请注释掉
int start = z.to;
for (int i = head[start]; ~i; i = edge[i].next) {
int end = edge[i].to;
if (dis[end] > dis[start] + edge[i].w) {
dis[end] = dis[start] + edge[i].w;
Q.push({edge[i].to,edge[i].next,dis[end]});
}
}
}
printf("%d\n", dis[t]);
}
int main() {
while(~scanf(" %d%d%d%d",&n,&m,&s,&t)) {
init();
for (int i = 0; i < m; i++) {
int from, to, w ;
scanf(" %d%d%d", &from, &to, &w);
addedge(from, to, w);
addedge(to, from, w);//无向图的话请加上
}
dijkstra(s,t);
}
return 0;
}
有向图例题
注意一下s+1,改一下输入输出就AC
无向图例题
模板为AC代码
2387 -- Til the Cows Come Home
按我说的该注释的注释掉,该加的加,修改一下输入输出就AC