讲了半天好像也许maybe听懂了一点,先写下来233
先整理整理怎么存(开始绕)
最简单的是邻接矩阵存,但是开到10000*10000就MLE了,所以我们用链式前向星存(据说是叫这个名字吧)
这是个什么鬼玩意呢?
我们在记录时,以输入的顺序记录。
我们记录一条边,就记下它的终点(to),权值(就是边长)(dis),以及和这条边的起点相同,编号稍微小一点的边的编号(next)(开始绕)
这里我们记录当前每个点的所有出边(就是起点是这个点的边)中编号最大的那一条边(因为上面的next是编号稍微小的边)
当然也可以依据自己的习惯存储边
先上段代码
int head[nmax],n,m,s;//head[i] 是 以 点 i 为 起 点 , 所 有 出 边 中 编 号 最 大 的 一 个 priority_queueint,int> > q; void add(int fr,int _to,int _dis) { cnt++; eage[cnt].to=_to; eage[cnt].dis=_dis; eage[cnt].next=head[fr];//fr 为 from 的 简 写 , 这 里 的 以 点 i 为 起 点 的 边 多 了 一 条, //所 以 上 一 个 以 点 i 为 起 点 的 编 号 最 大 的 边 就 是 这 里 的 以 i 为 起 点 编 号 最 大 的 边 的 上 一 条 边 head[fr]=cnt; //更 新 head[i] }Edge [50001]; const int inf=2147483647; int main() { scanf("%d%d%d",&n,&m,&o_node); dis[o_node]=0; for(int i=1;i<=m;i++) {int from,to,dis; cin>>from>>to>>dis; add(from,to,dis); }
这一坨是存图
拿张图举个例子
假设我们输入边的数据如下(三个数n,m,s,n为起点,m为终点,s为边长)
1 2 2
2 3 2
1 3 5
2 4 1
3 4 2
1 4 4
那代码中的存储如下
Edge[1].to=2,Edge[1].dis=2,Edge[1].next=0,head[1]=1(这里指没有上一条边),head[1]=1(这里head[i]记录的是以i为起点,当前最大编号出边的编号)
Edge[2].to=3,Edge[2].dis=2,Edge[2].next=0,head[2]=2
Edge[3].to=3,Edge[3].dis=5,Edge[3].next=1,head[1]=3
.....................................
讲完存图,再来说这个算法是怎么实现的
要求最短路径,这里有点类似贪心。
首先选择一个距离起点最近的直达点b,记录当前点与b的距离,再由b进行相同的扩展,来更新起点与其它点的距离
这样更新了一圈后就是最短距离,
再举个栗子
没错还是刚才那张图,这里标出了每条边的权值
按照dijkstra算法,我们首先找到距离①最近的直达点②,由②更新出①到④的最短路为3,①到③的最短路为4,
那么程序怎么实现呢?
看注释吧
(代码from gh,注释自己加的)
#include#include #include using namespace std; const int INF = 2147483647; struct edge { int to, dis_, next; } Edge[500001]; struct node { int to, dis; inline friend bool operator<(const node &a, const node &b) { return a.dis < b.dis;//构造函数,将优先队列按照权值从小到大排序 } }; int head[500001], dis[10001]; bool vst[10001]; int nodenum, edgenum, origin_node, cnt = 1; priority_queue q;//优先队列 inline void add_edge(int from, int to, int value) { Edge[cnt].to = to; Edge[cnt].dis_ = value; Edge[cnt].next = head[from]; head[from] = cnt++; } inline void dijkstra() { for (register int i = 1; i < origin_node; i++) { dis[i] = INF;//全部初始化为一个很大的数 } dis[origin_node]=0; for (register int i = origin_node + 1; i <= nodenum; i++) { dis[i] = INF; } q.push((node){origin_node, 0}); while (!q.empty())//队不空(这里是当广搜来做的) { int x = q.top().to; q.pop(); if (vst[x])//如果访问过,就跳过 continue; vst[x] = 1; for (register int i = head[x]; i; i = Edge[i].next)//从以x为起点的最后一条边开始,一直遍历完这个点的所有边 { dis[Edge[i].to] = min(dis[Edge[i].to], dis[x] + Edge[i].dis_);//比较原来的大小和以x点为中转后的大小(取小的) q.push((node){Edge[i].to, -dis[Edge[i].to]});//入队 } } } template inline T_ getnum() { T_ res = 0; bool flag = false; char ch = getchar(); while (!isdigit(ch)) { flag = flag ? flag : ch == '-'; ch = getchar(); } while (isdigit(ch)) { res = (res << 3) + (res << 1) + ch - '0'; ch = getchar(); } return flag?-res:res; } template inline void putnum(T_ num) { if (num<0) { putchar('-'); num=-num; } if (num>9)putnum(num/10); putchar('0'+num%10); } int main() { nodenum = getnum<int>(), dgenum = getnum<int>(),origin_node = getnum<int>(); for (register int i = 1; i <= edgenum; i++) { register int f, t, v; f = getnum<int>(), t = getnum<int>(), v = getnum<int>(); add_edge(f, t, v); } dijkstra(); for (register int i=1;i<=nodenum;putchar(' '),i++) { putnum<int>(dis[i]); } return 0; }
顺便附上一道dijkstra的题
一本通之城市路:
这个好像就是个模板哈
(代码from题解)
#include#include #include #include #include #include<string> #include #include #include<set> #include #define INF 0x3f3f3f3f #define PI acos(-1.0) #define N 3001 #define MOD 123 #define E 1e-6 using namespace std; struct node{ int pre; int next; int w; }a[N*10]; int n,m; int cnt; int head[N],vis[N],f[N]; void add(int x,int y,int w) { cnt++; a[cnt].pre=y; a[cnt].next=head[x]; a[cnt].w=w; head[x]=cnt; cnt++; a[cnt].pre=x; a[cnt].next=head[y]; a[cnt].w=w; head[y]=cnt; }//存图 int main() { cin>>n>>m; for(int i=1;i<=m;i++) { int x,y,w; cin>>x>>y>>w; add(x,y,w); } memset(f,INF,sizeof(f)); f[1]=0; vis[1]=1; int x=head[1];//手动模拟第一次出队 while(x!=0) { int y=a[x].pre; if(f[y]>a[x].w) f[y]=a[x].w; x=a[x].next; } int cnt=0; while(cnt<n)//遍历所有的点 { cnt++; int k; int minn=INF; for(int i=1;i<=n;i++) if(vis[i]==0&&f[i]<minn) { minn=f[i]; k=i; }//先把能赋值的距离赋值上 vis[k]=1; int x=head[k];//手动模拟for循环 while(x!=0)//这里木有队列,所以要while循环一次处理完 { int y=a[x].pre; int w=a[x].w; if(vis[y]==0&&f[y]>f[k]+w) f[y]=f[k]+w; x=a[x].next; } } if(f[n]==INF) cout<<"-1"<<endl; else cout< endl; return 0; }