最短路是图论部分经常坑熊孩子的部分,一般会和dp一起考。
求指定起点到图上所有点的最短路径
以洛谷 P3371 为例 地址 https://www.luogu.org/problemnew/show/P3371
最常用的方法 有
Floyd 复杂度O(n^3);
floyd的过程相当于对每个点都进行了三角形松弛操作,就是假如a->b+b->c > a->c,那么显然从a->b->c比从a->c优,然后就没了
因为该算法求出了所有点间的最短路
目测不能通过
//By AcerMo
#include
#include
#include
#include
#include
#include
using namespace std;
int a[5000][5000],n,m,st;//用邻接矩阵来存图即可,因为数组开大了会RE
inline int read()
{
int x=0;char ch=getchar();
while (ch>'9'||ch<'0') ch=getchar();
while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x;
}
inline void floyd()
{
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
{
if(i==k||a[i][k]==2147483647) continue;//优化
for(int j=1;j<=n;j++)
{
if(i==j||j==k||a[k][j]==2147483647) continue;
a[i][j]=min(a[i][j],a[i][k]+a[k][j]);//从i到j的当前最短路径与从i到k再到j的路径取min
}
}
return ;
}
signed main()
{
n=read(),m=read(),st=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
a[i][j]=2147483647;//初始化路径长为正无穷
for(int i=1,u,v,w;i<=m;i++)
{
u=read(),v=read(),w=read();
a[u][v]=min(a[u][v],w);
}
a[st][st]=0;floyd();
for(int i=1;i<=n;i++) printf("%d ",a[st][i]);
return 0;
}
之后是
Dijkstra 复杂度O(n^2)
目测数据太大也过不了
思想:通过一个已经确定最短路的点来更新与其相连的点的最短路
蓝白点思想,用两个颜色表示有没有确定最短路
代码
//By AcerMo
#include
#include
#include
#include
#include
#include
using namespace std;
const int inf=2147483647;
const int M=200500;
int m,n,st;
int dis[M];// 从起点到i点的最短路径 QAQ
bool jud[M];//判断颜色
//0->白色,已确定最短路径,1->蓝色,未被开发
int to[M],nxt[M],head[M],w[M],cnt;
inline int read()
{
int x=0;char ch=getchar();
while (ch>'9'||ch<'0') ch=getchar();
while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x;
}
inline void add(int x,int y,int z)
{
to[++cnt]=y;nxt[cnt]=head[x];w[cnt]=z;head[x]=cnt;
return ;
}
inline void dijkstra()
{
fill(jud,jud+n+1,1);//1说明没走过,蓝点=-=
fill(dis,dis+n+1,inf);//最短路径置成无穷
dis[st]=0;//起点到起点的路径长为0
while (1)//循环找起点通过每个点到其他点的最短路径
{
int v=-1;
for (int i=1;i<=n;i++)
if ((v==-1||dis[v]>dis[i])&&jud[i]) v=i;//距离起点目前最近的点
if (v==-1) break; //所有点都已找到最短路径
jud[v]=0;//该点标记为 白点
for (int i=head[v];i;i=nxt[i])
dis[to[i]]=min(dis[to[i]],dis[v]+w[i]);
}
return ;
}
signed main()
{
n=read();m=read();st=read();int x,y,z;
for (int i=1;i<=m;i++)
x=read(),y=read(),z=read(),add(x,y,z);
dijkstra();
for (int i=1;i<=n;i++) printf("%d ",dis[i]);
return 0;
}
Dijkstra可以使用优先队列优化,复杂度隐约是O(nlogn);可以通过
优先队列的用途是省去O(n)查找当前最短路,每次都将最短路放在队首
保证用图中距离起点最近的点去更新其他点
代码
//By AcerMo
#include
#include
#include
#include
#include
#include
using namespace std;
const int M=1000500;
struct heap
{
int to,cost;
bool friend operator < (heap a,heap b)
{
return a.cost>b.cost;
}
}add,now;
priority_queueq;
int n,m,st;
int dis[M],vis[M];
int to[M],nxt[M],head[M],w[M],cnt;
inline int read()
{
int x=0;char ch=getchar();
while (ch>'9'||ch<'0') ch=getchar();
while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x;
}
inline void adda(int x,int y,int z)
{
to[++cnt]=y;nxt[cnt]=head[x];w[cnt]=z;head[x]=cnt;
return ;
}
inline void dijkstra()
{
fill(dis,dis+n+1,2147483647);dis[st]=0;
add.to=st;add.cost=0;q.push(add);
while (q.size())
{
now=q.top();q.pop();
if (vis[now.to]||dis[now.to]!=now.cost) continue;vis[now.to]=1;
for (int i=head[now.to];i;i=nxt[i])
{
if (dis[to[i]]>dis[now.to]+w[i])
{
dis[to[i]]=dis[now.to]+w[i];
add.to=to[i];add.cost=dis[to[i]];q.push(add);
}
}
}
return ;
}
signed main()
{
n=read();m=read();st=read();int x,y,z;
for (int i=1;i<=m;i++)
x=read(),y=read(),z=read(),adda(x,y,z);
dijkstra();
for (int i=1;i<=n;i++) printf("%d ",dis[i]);
return 0;
}
Bellman-Ford算法 O(NE)还是会TLE
类似Floyd,用了一个松弛操作,即通过一个点来判断另外的点之间的边是否松弛,但是该算法枚举的是所有边;
//By AcerMo
#include
#include
#include
#include
#include
#include
using namespace std;
const int M=500500;
int n,m,st;
int dis[M],u[M],v[M],w[M];
inline int read()
{
int x=0;char ch=getchar();
while (ch>'9'||ch<'0') ch=getchar();
while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x;
}
inline int Bellman_Ford()
{
dis[st]=0;bool check=0,flag=0;
for(int k=1;k<=n-1;k++) //Bellman-Ford的算法
{
for(int i=1;i<=m;i++)
if((long long)dis[u[i]]+w[i]
O(NE)很快?NO!还存在一个叫做SPFA的算法,是Bellman-Ford算法的队列实现,可以将复杂度降到O(KE),但是如果建成的图为规则方阵,则会比较慢,现在都8102年了,还是去用heap+dij吧
SPFA O(KE)
每次都从队列里取出一个元素,修改与它临近的所有点的最小值,修改成功,则入队
//By AcerMo
#include
#include
#include
#include
#include
#include
using namespace std;
const int M=500500;
struct edge{int to,cost;}t;
int dis[M],vis[M];
int n,m,st;
vectorv[M];//邻接表存储
inline int read()
{
int x=0;char ch=getchar();
while (ch>'9'||ch<'0') ch=getchar();
while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x;
}
inline void SFPA()
{
fill(dis,dis+n+1,2147483647);//fill函数类似memset,填充指定区间
dis[st]=0;queueq;q.push(st);
while(q.size())//队列不为空即点还没有修改完
{
int u=q.front();q.pop();vis[u]=0;//掰回没到过
for(int i=0;idis[u]+pay)//松弛
{
dis[go]=dis[u]+pay;
if(!vis[go])//加入队列
vis[go]=1,q.push(go);
}
}
}
return ;
}
int main()
{
n=read();m=read();st=read();int x;
for(int i=1;i<=m;i++)
{
x=read();t.to=read();t.cost=read();
v[x].push_back(t);
}
SFPA();
for (int i=1;i<=n;i++) printf("%d ",dis[i]);
return 0;
}
依据数据大小使用;
PS Floyd,Dijkstra算法无法判断存在负权问题