一个因为邻接表写的太复杂而弃坑转向链式前向星的蒟蒻,不过虽然链式前向星的名字很鬼畜但存图效果是真的可以。
链式前向星其实原理和邻接表差不多只不过前者用数组来连接后者用指针而已。
链式前向星结构体内容:
struct l{
int next;//存储与该边同起点的边的编号
int to;//当前边的终点的顶点下标
int w;//当前边的边权值
}edge[500005];
这里的next其实就是我们链表中的next指针不过稍微有一点点不同。
先放存图代码:
int head[10005];
void add(){//链式前向星存图
for(int i=0;i<b;i++){
int n,m,k; cin>>n>>m>>k;//n,m,k表示n到m的边的边权值为k
edge[i].to=m;
edge[i].w=k;
edge[i].next=head[n];
head[n]=i;
}
}
只给代码刚开始看很懵不过等我们带值进去走两遍就明白原理了。
输入第一条边:编号当然是1: 1 2 2
此时edge【1】.to=2,edge【1】.w=2,edge【1】.next=head【1】(因为是输入的第一条边所以此时的next其实是为空的) head[1]=1;(将以顶点一为起点的边1的编号存入)
第二条边编号2:1 3 5
此时edge【2】.to=2,edge【2】.w=2,edge【2】.next=head【1】(将同起点的上一条边的编号存入当前next,使得当前边与它上一同起点的边形成连接)head[1]=2; (更新当前顶点边的编号为下一条边做准备)…
大致就是这样我们把以同一顶点为起点的边联系在了一起(简直就是邻接表的孪生兄弟)
接下来是dijkstra
原理图:
大致就是说以A为源点所以到A的距离为0,然后再以A为起点更新A到其他点的距离(AB=10,AC=3),在当前的所有边中找最短边的终点(已经找过的如A,便不再找)再以它为中转点更新各顶点到源点的边长(如A-C-B=7<A-B=10,所以A-B更新为7),以此类推直到所有点被找过。
dijkstra代码实现:
int dis[10005];//记录源点到各顶点的最短距离
bool vis[10005]; //记录各顶点的访问情况
void dij(){
memset(dis,lll,sizeof dis);
dis[c]=0;
vis[c]=true;
for(int i=head[c];i!=-1;i=edge[i].next){
dis[edge[i].to]=min(dis[edge[i].to],edge[i].w);
}//以源点为起点更新边值
for(int i=1;i<=a-1;i++){
int mm=lll,post;
for(int j=1;j<=a;j++){//寻找当前最短边及其对应终点
if(dis[j]<mm&&vis[j]==false){
mm=dis[j],post=j;
}
}
vis[post]=true;
for(int j=head[post];j!=-1;j=edge[j].next){//以该点为中转点更新边值
if(edge[j].w+dis[post]<dis[edge[j].to]){
dis[edge[j].to]=edge[j].w+dis[post];
}
}
}
}
完整代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define lll 99999999//不知道为什么这里用0x3f3f3f3f会wa一个点
struct l{
int next;//存储与该边同起点的边的编号
int to;//当前边的终点的顶点下标
int w;//当前边的边权值
}edge[500005]; int a,b,c;
int head[10005];
void add(){//链式前向星存图
for(int i=0;i<b;i++){
int n,m,k; cin>>n>>m>>k;//n,m,k表示n到m的边的边权值为k
edge[i].to=m;
edge[i].w=k;
edge[i].next=head[n];
head[n]=i;
}
}
int dis[10005];//记录源点到各顶点的最短距离
bool vis[10005]; //记录各顶点的访问情况
void dij(){
for(int i=1;i<=10000;i++) dis[i]=lll;
dis[c]=0;
vis[c]=true;
for(int i=head[c];i!=-1;i=edge[i].next){
dis[edge[i].to]=min(dis[edge[i].to],edge[i].w);
}//以源点为起点更新边值
for(int i=1;i<=a-1;i++){
int mm=lll,post;
for(int j=1;j<=a;j++){//寻找当前最短边及其对应终点
if(dis[j]<mm&&vis[j]==false){
mm=dis[j],post=j;
}
}
vis[post]=true;
for(int j=head[post];j!=-1;j=edge[j].next){//以该点为中转点更新边值
if(edge[j].w+dis[post]<dis[edge[j].to]){
dis[edge[j].to]=edge[j].w+dis[post];
}
}
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin>>a>>b>>c;
for(int i=1;i<=10000;i++){//表示当前所有顶点都没有边连接
head[i]=-1;
}
add();
dij();
for(int i=1;i<=a;i++){
if(dis[i]==lll) cout<<"2147483647"<<" ";
else cout<<dis[i]<<" ";
}
}