存储图
可用邻接矩阵和邻接表存储,当图中点个数不超过1000时都可用邻接矩阵。
遍历图
- dfs
直接递归
int G[max][max];//邻接矩阵
int n;//顶点
bool vis[max]={false};
void dfs(int a){
vis[a]=true;
for(int i=0;i<n;i++){
if(G[a][i]!=inf&&vis[i]==false){
dfs(i);
};
};
}
void set_dfs(){
for(int i=0;i<n;i++){
if(vis[i]==false){
dfs(i);
};
};
}
//确保每个连通块都被访问
- bfs
用辅助队列
int G[max][max];
int n;
int vis[max]={false};//顶点曾入过队列
void bfs(s){
queue<int>q;
q.push(s);
while(!q.empty()){
int t=q.front();
q.pop();
for(int i=0;i<n;i++){
if(G[t][i]!=inf&&vis[i]==false){
q.push(i);
vis[i]==true;
};
};
};
}
void set_bfs(){
for(int i=0;i<n;i++){
if(vis[i]==false){
bfs(i);
};
};
}//遍历所有连通块
Dijkstra
即起点固定,从起点到其他点的路径记录保持最短。
int n;
int G[max][max];
int vis[max];//检测是否已经遍历过该点
int d[max];//每个点到起点的最短距离
int s;//起点
//1.记录路径int pre[max];
//2.记录边权 int cost[max][max];
//2.每个点到起点最短路径的最优边权和 int c[max];
//3.记录点权 int weight[max];
//3.每个点到起点最短路的最优点权和 int w[max];
//4.每个点到起点最短路的条数 int num[max];
void dijkstra(){
fill(vis,vis+n,0);
fill(d,d+n,inf);//初始化
d[s]=0;
//2.边权初始化 c[s]=0;
//3.点权初始化
//fill(w,w+n,0);
//w[s]=weight[s];
//4.最短路初始化
//fill(num,num+n,0);
//num[s]=1;
for(int i=0;i<n;i++){//遍历n次
int min;
int u=-1;
for(int j=0;j<n;j++){
if(vis[i]==0&&d[i]<min){
min=d[i];
u=i;
};
};
if(u==-1)return;
vis[u]=1;
//以u为中介点开始优化
for(int j=0;j<n;j++){
if(vis[j]==0&&G[u][j]!=inf&&d[j]<G[u][j}+d[u]){
d[j]=G[u][j]+d[u];
//1.pre[j]=u;
//2.c[j]=c[u]+cost[u][j];
//3.w[j]=w[u]+weight[j];
//4.num[j]=num[u];
};
//2.if(vis[j]==0&&G[u][j]!=inf&&d[j]==G[u][j]+d[u]){
// if(c[j]>c[u]+cost[u][v]){
// c[j]=c[u]+cost[u][v];
// };
// };
//3.if(vis[j]==0&&G[u][j]!=inf&&d[j]==G[u][j]+d[u]){
// if(w[j]<w[u]+weight[j]){
// w[j]=w[u]+weight[j];
// };
// };
//4.if(vis[j]==0&&G[u][j]!=inf&&d[j]==G[u][j]+d[u]){
// num[j]+=num[u];
// };
};
额外要求:
-
记录从s到e(任意指定一终点)的最短路径
设置记录前驱的数组pre[],令pre[v]表示从s到v最短路上v的前一个节点。 -
在路径最短条件下新增边权最少
将题目输入的边权信息计入cost[u][v]中,并新开数组d记录每个点到起点最短路的最优边权。 -
在路径最短条件下新增点权最多
将题目输入的点权信息计入weight[]中,并新开数组w记录每个点到起点最短路中的最优点权。 -
计算最短路径条数
新增数组num,记从起点到顶点u的最短路径条数为num[u].初始化只有起点的num[s]=1,其余都为0.
floyd
floyd为解决全源最短路问题,即对给定的任意两点得出它们之间的最短路径。
由于算法复杂度限制,当顶点数在200以内时可用邻接矩阵实现floyd。
流程:
** 先枚举中介点k,再枚举所有顶点对i,j。**观察是否以k为中介点能使i,j之间路径长度改善。
int n;//顶点数
int m;//边数
int div[max][max];//顶点ij的最短距离
fill(div[0],div[0]+max*max,inf);//初始化,inf表示不是通路,无需div[i][i]=0,会干扰判断.
void floyd(){
for(int k=0;k<n;k++){
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(dis[k][j]!=inf&&dis[i][k]!=inf&&dis[i][j]<dis[i][k]+dis[k][j]){
dis[i][j]=dis[i][k]+dis[k][j];
};
};
};
};
}
最小生成树
- 性质
连通图中所有点的前提下边权最小。
最小生成树可能不唯一,但边权之和一定唯一。
最小生成树在无向图上生成。 - prim算法
对图G设置集合S,存放已被访问的顶点,然后每次从剩下的点集合中选择与集合S最短距离最小的一个顶点,访问该点并加入S。之后以该点为中介点,优化所有该点能达到的点与集合s之间的最短距离。
执行上述操作直到所有点已被访问。
代码与dijkstra相似
int n;//顶点数
int G[max][max];//存放边权
int d[max];//顶点与集合s的最短距离
int vis[max]={0};//标记数组
//假设0为给出最小生成树根节点
int prim(){
//初始化
fill(d,d+max,inf);
d[0]=0;
int sum=0;//存放最小生成树边权之和
for(int i=0;i<n;i++){
int min=inf;
int u=-1;
for(int j=0;j<n;j++){
if(vis[j]==0&&d[j]<min){
u=j;
min=d[j];
};
};
if(u==-1){return -1;};
vis[u]=1;
sum+=d[u];//将与集合的最小距离的边加入最小生成树
for(int v=0;v<n;v++){//以u为中介点更新其余点到集合s的距离
if(vis[v]==0&&G[u][v]!=inf&&G[u][v]<d[v]){
d[v]=G[u][v];
};
};
};
return sum;
}
-
kruskal算法
使用并查集+排序
先按边权大小从小到大排序。再测试每条边,如果两个顶点不属于同一连通块,则加入最小生成树,否则将边舍弃。
当所有边测试完或者最小生成树中边数等于顶点数-1时结束程序。
而当结束时,最小生成树中的边数小于总顶点数-1,则该图不连通。判定函数结束条件:遍历完所有边。
剪枝条件:当前最小生成树边数已满足总顶点数-1。
判定不连通条件:遍历完边后,当前最小生成树边数不满足总顶点数-1。
struct edge{
int u,v;//边的两个顶点
int cost;//边权
}
//边数组
vector<edge>list;
//并查集数组
int f[max];
//并查集
int findf(int a){
int root=a;
while(root!=f[root]){
root=f[root];
};
//压缩
while(f[a]!=root){
int t=f[a];
f[a]=root;
a=t;
};
return root;
}
//n为顶点数,m为边数
int krus(int n,int m){
int sum=0;
int mm=0;//当前生成树的边数
for(int i=1;i<=n;i++){
f[i]=i;
};//并查集数组初始化
sort(list.begin(),list.end(),cmp);//按边大小排序
//遍历所有边
for(int i=0;i<list.size();i++){
if(findf(list[i].u)!=findf(list[i].v)){
f[findf(list[i].u)]=f[findf(list[i].v)];//合并
sum+=list[i].cost;
mm++;
if(mm=n-1){break;};
};
};
if(mm!=n-1)return -1;
else return sum;
}
5.边少用kruskal,边多用prim。