最短路
多源最短路径
Floyed-Warshall算法
\[弗洛伊德算法,是最简单的多元最短路径算法,可以计算图中任意两点的最短路径。 时间复杂度为 O(N^3)
\]
算法描述:
设置\(dis[u][v]\) 表示从\(u\)到\(v\)的距离
(1) 初始化
将相连接的点的距离设置为 \(dis[u][v]\)=\(dis[v][u]\)
如果不相连,则\(dis[u][v]=inf\)
(2) 通过中间节点进行拓展
for(int k=1;k<=n;++k){
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j){
if(dis[i][j]>dis[i][k]+dis[k][j]&&dis[i][k]!=INF&&dis[k][j]!=INF){
dis[i][j]=dis[i][k]+dis[k][j];
}
}
}
}
(3) 最终的出来的 dis[u][v] 则为u到v的最短路径
Floyed 变形
如果图为无边权图,且题求连通性,可以将 dis[u][v]=1,(相连) dis[u][v]=0(不相连)
for(int k=1;k<=n;++k){
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j){
dis[i][j]|=(dis[i][k]&&dis[k][j]);
}
}
}
Dijkstra 算法
\[Dijkstra 算法是一种单源最短路径算法,也就是说单次只能计算从一个起点到另外一个起点的最短路,时间复杂度是 O(N^2) ,但是不能处理负边权问题。
\]
算法描述:
设置起点为 s,那么dis[u] 则表示从s到v的最短路径,book[u] 则判断一个点是否被拓展过.(可以通过pre[u]记录前驱节点从而记录路径)
(1)初始化
\[dis[u]=inf \ \ \ u=1,2,3.....n(u\neq s)
\\
book[u]=0 \ \ \ u=1,2,3.....n
\\
dis[s]=0
\]
(2) 拓展节点
a.从未被访问的节点中找出一个顶点使得 \(dis[u]\) 是最小的
b.将\(u\)标记为确定最短路
c.对于每一个与\(u\)相连接的节点进行拓展更新最短路径
for(int i=1;i<=n;++i){
int minx=INF;
int op=-1;
for(int j=1;j<=n;++j){
if(rdis[j]<minx && book[j]!=1){
minx=rdis[j];
op=j;
}
}
if(op!=-1){
book[op]=1;
for(int j=1;j<=n;++j){
if(rdis[j]>rdis[op]+dis[op][j]&&!book[j]){
rdis[j]=rdis[op]+dis[op][j];
fa[j]=op;
}
}
}
}
(3) 最终求得的 dis[v] 则是s到v的最短路径
Dijkstra 算法优化
我们发现,对于每一次寻找最小值上述过程使用的都是遍历整个数组,但事实上我们可以不用遍历整个数组寻找最小值,对于最小值的寻找我们有着多种方法。
堆优化
我们只需要维护一个小根堆即可求得上述答案
#include<bits/stdc++.h>
using namespace std;
const int size=100010;
int n,m,s;
int head[size],ver[2*size],Next[2*size],edge[2*size],tot;
int dis[size],v[size];
struct node{
int dis;
int num;
bool operator <(const node &x) const{
return x.dis<dis;
}
};
priority_queue<node>q;
void add(int x,int y,int z){
ver[++tot]=y;edge[tot]=z;Next[tot]=head[x];head[x]=tot;
}
void dija(){
memset(dis,0x3f,sizeof(dis));
memset(v,0,sizeof(v));
dis[s]=0;q.push((node){0,s});
while(q.size()){
int x=q.top().num;q.pop();
if(v[x]) continue;v[x]=1;//只能扩展一次
for(int i=head[x];i;i=Next[i]){
int y=ver[i];
if(dis[y]>dis[x]+edge[i]){
dis[y]=dis[x]+edge[i];
if(!v[y]){q.push((node){dis[y],y});}//这里不需要标记
}
}
}
}
int main(){
scanf("%d %d %d",&n,&m,&s);
for(int i=1;i<=m;++i){
int x,y,z;
scanf("%d %d %d",&x,&y,&z);
add(x,y,z);
}
dija();
for(int i=1;i<=n;++i){
printf("%d ",dis[i]);
}
return 0;
}
线段树优化
#include<bits/stdc++.h>
using namespace std;
const int size=100010;const int N=100010,M=200010;
inline long long read(){
long long x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
struct node{
long long l,r,data,num;
}t[4*N];
int n,m,num,x,y,z,s,tot;
long long k;
const long long inf=1e18;
bool v[N];
int head[N],ver[M],Next[M],poi[N];
long long edge[M],d[N];
void add(int x,int y,int z){
ver[++tot]=y;edge[tot]=z;Next[tot]=head[x],head[x]=tot;
}
void build(long long p,long long l,long long r){
t[p].l=l,t[p].r=r;t[p].data=inf;
if(l==r){t[l].num=p;return;}
long long mid=(l+r)>>1;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
}
void change(long long p,long long v){
p=t[p].num;t[p].data=v;
while(p>>=1,p){
t[p].data=min(t[p*2].data,t[p*2+1].data);
}
}
long long ask(){
int p=1;
while(t[p].l!=t[p].r){
if(t[p*2].data<t[p*2+1].data) p<<=1;
else p=p*2+1;
}
return t[p].l;
}
int main(){
n=read();m=read();s=read();
for(int i=1;i<=m;++i){
x=read();y=read();z=read();
add(x,y,z);
}
for(int i=1;i<=n;++i){
d[i]=inf;
}
d[s]=0;build(1,1,n);
change(s,0);
for(int i=1;i<=n;++i){
int x=ask();
if(d[x]==inf) break;
for(int j=head[x];j;j=Next[j]){
int y=ver[j];
if(d[y]>d[x]+edge[j]){
d[y]=d[x]+edge[j];
change(y,d[y]);
}
}
change(x,inf);
}
for(int i=1;i<=n;++i){
if(d[i]!=inf) printf("%lld\n",d[i]);
else printf("-1\n");
}
return 0;
}