dijkstra可以求出一个点到所有点的最短路。
链式前向星其实就是静态建立的邻接表,时间效率为O(m),空间效率也为O(m)。遍历效率也为O(m)。
【模板】单源最短路径(弱化版)
题目:P3371 【模板】单源最短路径(弱化版) - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题解: 1.首先明确这是一个有向图求最短路的问题,这个题目我用的是链式前向星dijkstra算法。
2.4个数组,结构体lu[]中的to代表这个点指向的这一个点,next表示与这个边起点相同的上一条边的编号,v代表权重。
head[](存储以 i 为起点的最后一条边的编号),head数组与lu[]相关。
dis[](记录权重的数组),
v[](记录这个点的所有边是否都考虑完的数组);
3.初始化数组,dis[]变成无限大,v[]变成没有访问的状态
4.将出发点的距离设置成0,标记一下出发点,找到与出发点有关的点,判断一下点是否被访问过,更新相关点的dis的值。然后找下一个出发点(这个点没有被标记过且dis[]数组中值最小的)。一直循环到最后一个点的距离更新。
6.最后的dis[]数组中存的数就是一开始的出发点到所有点的最小路的长度。
代码如下
#include<bits/stdc++.h>
using namespace std;
int cnt,head[10009],n,m,s,dis[10009],v[10009];
#define max 2147483647
struct node{
int to,next,dis;
}lu[500009];//邻接表存图
void add(int u, int v, int w)//加边,u起点,v终点,w边权
{
cnt++;
lu[cnt].to = v; //终点
lu[cnt].dis = w; //权值
lu[cnt].next = head[u];//以u为起点上一条边的编号,也就是与这个边起点相同的上一条边的编号,防止覆盖原来的值
head[u] = cnt;//更新以u为起点上一条边的编号
}
int main()
{
cin>>n>>m>>s;
int x,y,z;
int i,j;
for(i=1;i<=n;i++)
{
dis[i]=max;//dis[]变成无限大,v[]变成没有访问的状态
v[i]=0;
}
for(i=1;i<=m;i++)
{
cin>>x>>y>>z;
add(x,y,z);//有向图
}
int ans;
dis[s]=0;
ans=s;
for(i=1;i<=n;i++)
{
v[ans]=1;
for(j=head[ans];j!=0;j=lu[j].next)//找到最小的距离
{
int a=ans,b=lu[j].to,c=lu[j].dis;
if(dis[b]>dis[a]+c&&v[b]==0)//没有访问过
{
dis[b]=dis[a]+c;
}
}
long long int min = max;//找下一个出发点
for(x=1;x<=n;x++)
{
if (v[x]==0&&min>dis[x])
{
min=dis[x];
ans=x;
}
}
}
for(i=1;i<=n;i++)
cout<<dis[i]<<' ';
return 0;
}
[USACO09OCT]Heat Wave G
题目:P1339 [USACO09OCT]Heat Wave G - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题解: 1.首先明确这是一个无向图求两个点之间最短路径。第一想到的是Floyd算法,但是Floyd会超时。然后就想到了dijkstra算法,尝试了朴素版的,结果只能ac50,然后就换成了链式前向星dijkstra算法。
2.与上面的题是一样的,不过多赘余,改一下输入,输出就可以了。因为是无向图所以add函数要调用两次。
ac100代码如下
#include<bits/stdc++.h>
using namespace std;
int cnt,head[10009],n,m,s,g,dis[10009],v[10009];
#define max 2147483647
struct node{
int to,next,dis;
}lu[500009];//邻接表存图
void add(int u, int v, int w)//加边,u起点,v终点,w边权
{
cnt++;
lu[cnt].to = v; //终点
lu[cnt].dis = w; //权值
lu[cnt].next = head[u];//以u为起点上一条边的编号,也就是与这个边起点相同的上一条边的编号,防止覆盖原来的值
head[u] = cnt;//更新以u为起点上一条边的编号
}
int main()
{
cin>>n>>m>>s>>g;
int x,y,z;
int i,j;
for(i=1;i<=n;i++)
{
dis[i]=max;
v[i]=0;
}
for(i=1;i<=m;i++)
{
cin>>x>>y>>z;
add(x,y,z);
add(y,x,z);//无向图
}
int ans;
dis[s]=0;
ans=s;
for(i=1;i<=n;i++)
{
v[ans]=1;
for(j=head[ans];j!=0;j=lu[j].next)//找到最小的距离
{
int a=ans,b=lu[j].to,c=lu[j].dis;
if(dis[b]>dis[a]+c&&v[b]==0)//没有访问过
{
dis[b]=dis[a]+c;
}
}
long long int min = max;
for(x=1;x<=n;x++)
{
if (v[x]==0&&min>dis[x])
{
min=dis[x];
ans=x;
}
}
}
cout<<dis[g];
return 0;
}
这里说一下朴素版dijkstra。
与上面的原理一样,我只要是说一下代码的区别
1.这里是用二维数组r[][]来存储所有权重的消息,其他的是一样的。但是二维数组会比较耗内存。
2.这里要初始化r[][[]数组消息,除了到本身,其实全部变成无限大值。
3.for (i = 1; i <= n; i++) { dis[i] = r[s][i]; },这一步是把dis[]数组的值变成与出发点距离有关的值。
4.与上面不一样的是这个是先找到与出发点有关的最小距离的值,设置成下一个出发点,然后再更新dis[]数据。
#include<bits/stdc++.h>
using namespace std;
int r[2525][2525],dis[2525],v[2525];//记录权重的数组
int n,m,s,g;
int main() {
cin>>n>>m>>s>>g;
int max=1009;
int i, j, k,z,y,x,ans;
for (i = 1; i <= n; i++) {
for (j = 1; j <= n; j++) {
if (i == j)
r[i][j] = 0;
else
r[i][j] = max;
}
}
for (i = 1; i <= m; i++) {
cin >> x >> y >> z;
r[x][y] = z;
r[y][x] = z;//无向图
}
for (i = 1; i <= n; i++) {
dis[i] = r[s][i];
v[i]=0;
}
v[s] = 1;
for (i = 1; i <= n ; i++) {
int min=max;//找到与出发点有关的最小距离的值,设置成下一个出发点
for (j = 1; j <= n; j++) {
if (v[j] == 0 && dis[j] < min) {
min = dis[j];
ans = j;
}
}
v[ans] = 1;//更新
for (k = 1; k <= n; k++) {
if (dis[k] > dis[ans] + r[ans][k])
dis[k] = dis[ans] + r[ans][k];
}
}
cout<<dis[g];
return 0;
}
【模板】单源最短路径(标准版)
题目:P4779 【模板】单源最短路径(标准版) - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题解: 1.与第一个题相同的题目,这个题要用链式前向星dijkstra算法+优先队列。
2.与上面的思路相同这里只要讲一下优先队列。自定义优先队列的排序结构。
struct node1{
int x;
int y;
bool operator<(const node1&x)const{return y>x.y;}//小顶堆
};
priority_queue<node1>c;
c.push({s,0});//入队,优先对列按dis[](即结构体中的y)数组从小到大排序
t=c.top().x;//这里取出的是上面的s。
c.pop();//出队
//这里的int x;
int y;代表要向队列中传入两个数
3.这里循环与t有关的点,更新这些点的dis[]的值,如果没有被访问过就入队。
for(i=head[t];i!=0;i=lu[i].next)
{
int a=t,b=lu[i].to,v1=lu[i].d;
if(dis[b]>dis[a]+v1)
{
dis[b]=dis[a]+v1;
if(v[b]==0)
c.push({b,dis[b]});//入队
}
}
代码如下
#include<bits/stdc++.h>
#include<queue>
using namespace std;
int cnt,n,m,s,dis[100009],v[1000009],head[100009];
#define max 2147483647
struct node1{
int x;
int y;
bool operator<(const node1&x)const{return y>x.y;}//小顶堆
};
struct node{
int to,next,d;
}lu[200009];//邻接表存图
void add(int u, int v, int w)//加边,u起点,v终点,w边权
{
cnt++;
lu[cnt].to = v; //终点
lu[cnt].d = w; //权值
lu[cnt].next = head[u];//以u为起点上一条边的编号,也就是与这个边起点相同的上一条边的编号,防止覆盖原来的值
head[u] = cnt;//更新以u为起点上一条边的编号
}
int main()
{
cin>>n>>m>>s;
int x,y,z;
int i,j;
for(i=1;i<=n;i++)
{
dis[i]=max;
v[i]=0;
}
for(i=1;i<=m;i++)
{
cin>>x>>y>>z;
add(x,y,z);//有向边
}
priority_queue<node1>c;
dis[s]=0;
c.push({s,0});//入队,优先对列按dis[]即结构体中的y数组从小到大排序
int t;
while(!c.empty())
{
t=c.top().x;
c.pop();//出队
if(v[t])
continue;
v[t]=1;
for(i=head[t];i!=0;i=lu[i].next)
{
int a=t,b=lu[i].to,v1=lu[i].d;
if(dis[b]>dis[a]+v1)
{
dis[b]=dis[a]+v1;
if(v[b]==0)
c.push({b,dis[b]});//入队
}
}
}
for(i=1;i<=n;i++)
cout<<dis[i]<<' ';
return 0;
}
邮递员送信
题目:P1629 邮递员送信 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题解: 1.首先明确这是一个有向图求最短路的问题,很简单只要把来的值算一遍,去的值算一遍加在一起就可以了,我用的是链式前向星dijkstra算法+优先队列。
2.一开始我求回来的值用的是循环,结果时间超限了,怎么办?把邻接矩阵倒过来!再开辟一个相同的空间,记录回来的值不就好了。其他的过程都是一样的,这样的话回来就不要循环了。
3.因为时间超限这里我加了快读。
4.因为要调用两次dij()函数,所以在dij()中对dis[]和v[]初始化。且注意循环的最后值。
代码如下
#include<bits/stdc++.h>
#include<queue>
using namespace std;
int cnt,n,m,dis[100009],v[1000009],head[100009];
#define max 2147483647
int read()//快读
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*f;
}
struct node1{
int x;
int y;
bool operator<(const node1&x)const{return y>x.y;}//小顶堆
};
priority_queue<node1>c;//优先队列
struct node{
int to,next,d;
}lu[200009];//邻接表存图
void add(int u, int v, int w)//加边,u起点,v终点,w边权
{
cnt++;
lu[cnt].to = v; //终点
lu[cnt].d = w; //权值
lu[cnt].next = head[u];//以u为起点上一条边的编号,也就是与这个边起点相同的上一条边的编号,防止覆盖原来的值
head[u] = cnt;//更新以u为起点上一条边的编号
}
void dij(int s)
{
int i;
for(i=1;i<=2*n;i++)
{
dis[i]=max;
v[i]=0;
}
dis[s]=0;
c.push({s,0});
int t;
while(!c.empty())
{
t=c.top().x;
c.pop();
if(v[t])
continue;
v[t]=1;
for(i=head[t];i!=0;i=lu[i].next)
{
int a=t,b=lu[i].to,v1=lu[i].d;
if(dis[b]>dis[a]+v1)
{
dis[b]=dis[a]+v1;
if(v[b]==0)
c.push({b,dis[b]});
}
}
}
}
int main()
{
n=read();m=read();
int x,y,z;
int i,j;
for(i=1;i<=m;i++)
{
x=read(),y=read(),z=read();
add(x,y,z);//有向边
add(y+n,x+n,z);
}
dij(1);
int ans=0;
for(i=2;i<=n;i++)
ans+=dis[i];//去的路程
dij(1+n);
for(i=2+n;i<=2*n;i++)
ans+=dis[i];
cout<<ans;
return 0;
}