1.spfa算法
3.Floyd算法
求多源最短路。
用邻接矩阵把图存下来,然后三重循环。
第一重循环,k从1到n,第二重循环i从1到n,第三重循环j从1到n。每次更新一遍。d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
循环之后,di存的就是从i到j的最短路径长度,原理基于动态规划。
d[k,i,j]表示从i出发,只经过1~k这些中间点到达j的最短距离。循环中首先枚举k,因为k表示阶段
3.Prim算法求最小生成树
跟dijkstra算法很相似。
用一个集合S表示当前在连通块中的所有点。
先把所有距离初始化为正无穷,接下来n次迭代,每次迭代执行如下步骤:
1.找到不在集合当中的、距离最小的点。
2.用t更新其他点到集合的距离。
这个距离指的是一个点连接到集合元素当中的长度最小的边。如果不存在则设为正无穷
最小生成树的边指的就是这些最小边。这些点和边的连接体就是最小生成树。
3.St[t]=true 把t加入到集合当中。
如果存在重边,一般是保留最小的边。
给定一个 nn 个点 mm 条边的无向图,图中可能存在重边和自环,边权可能为负数。
求最小生成树的树边权重之和,如果最小生成树不存在则输出
impossible
。给定一张边带权的无向图 G=(V,E)G=(V,E),其中 VV 表示图中点的集合,EE 表示图中边的集合,n=|V|n=|V|,m=|E|m=|E|。
由 VV 中的全部 nn 个顶点和 EE 中 n−1n−1 条边构成的无向连通子图被称为 GG 的一棵生成树,其中边的权值之和最小的生成树被称为无向图 GG 的最小生成树。
输入格式
第一行包含两个整数 nn 和 mm。
接下来 mm 行,每行包含三个整数 u,v,wu,v,w,表示点 uu 和点 vv 之间存在一条权值为 ww 的边。
若存在最小生成树,输出最小生成树边权重之和,否则输出impossible
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=510,INF=0x3f3f3f;//定义无穷大
int n,m;
int g[N][N];
int dist[N];
bool st[N];
int prim(){
memset(dist,0x3f,sizeof dist);
int res=0;//存生成树中最小长度边之和
for(int i=0;i<n;i++){
int t=-1;
for(int j=1;i<=n;j++){
if(!st[j]&&(t==-1||dist[t]>dist[j])){
t=j;
}
}
if(i&&dist[t]==INF){//如果不是第一个点并且dist为正无穷说明最小距离是不存在的
return INF;
}
for(int j=1;j<=n;j++) dist[j]=min(dist[j],g[t][j]);//更新其他的点到集合的距离
if(i) res+=dist[t];//否则只要不是第一个点就把dist加入到答案长度中
st[t]=true;//加入到集合中
}
}
int main(){
scanf("%d%d",&n,&m);
memset(g,0x3f,sizeof g);
while(m--){
int a,b,c;
scanf("%d%d%d",&a,&b,c&);
g[a][b]=g[b][a]=min(g[a][b],c);//无向边。去重
}
int t=prim();
if(t==INF) puts("impossible");
else printf("%d\n",t);
return 0;
}
三十.Kruskal算法求最小生成树
1.先将所有边按照权重从小到大排序
2.枚举每条边ab和权重c。如果ab不连通(就是这条边对应的某个点不在集合当中),将这条边也加入集合中
因为思路很简单而且执行很快,所以在稀疏图中只要用这个算法就可以。
给定一个 nn 个点 mm 条边的无向图,图中可能存在重边和自环,边权可能为负数。
求最小生成树的树边权重之和,如果最小生成树不存在则输出
impossible
。给定一张边带权的无向图 G=(V,E)G=(V,E),其中 VV 表示图中点的集合,EE 表示图中边的集合,n=|V|n=|V|,m=|E|m=|E|。
由 VV 中的全部 nn 个顶点和 EE 中 n−1n−1 条边构成的无向连通子图被称为 GG 的一棵生成树,其中边的权值之和最小的生成树被称为无向图 GG 的最小生成树。
输入格式
第一行包含两个整数 nn 和 mm。
接下来 mm 行,每行包含三个整数 u,v,wu,v,w,表示点 uu 和点 vv 之间存在一条权值为 ww 的边。
输出格式
共一行,若存在最小生成树,则输出一个整数,表示最小生成树的树边权重之和,如果最小生成树不存在则输出
impossible
。
#include<iostream>
#include<algorithm>
using namespace std;
const int N=200010;
int n,m;
int p[N];
struct Edge{//用结构体来存储边
int a,b,w;
bool operator< (const Edge &W)const{//重载小于号,按照权重来比较大小
return w<W.w;
}
} edges[N];
int main(){
scanf("%d%d",&n,&m);
for(int i=0;i<m;i++){
int a,b,w;
scanf("%d%d%d",&a,&b,&w);
edges[i]={a,b,w};
}
sort(edges,edges+m);//排序所有边
for(int i=1;i<=n;i++) p[i]=i;//并查集的初始化
for(int i=0;i<m;i++){
int a=edges[i].a,b=edges[i].b,w=edges[i].w;
a=find(a),b=find(b);//并查集里的find函数
if(a!=b){//如果两个点不在一个集合中(他们的祖宗节点不同
p[a]=b;//合并到集合中
res+=w;//res中存放最小生成树的边权重和
cnt++;//存当前加入了多少条边
}
}
if(cnt<n-1) puts("impossible");//如果集合中边的总数小于n-1,说明树是不连通的
else printf("%d\n",res);
}