目录
图的遍历
DFS算法(深度优先)
在一个连通块里遍历:
void DFS(int r){
vis[r] = true;
for(int i=0;i<num;i++){
if(vis[i] == false && G[r][i] < INF)
DFS(i);
}
}
可以在这部分加上深度等信息。
遍历所有的连通块:
void DFSTrave(G){
for(int i=0;i<num;i++){
if(vis[i] == false)
DFS(i);
}
}
可以在这部分计算有几个连通块。
BFS算法(广度优先)
void BFS(int r){
queue<int> q;
q.push(r);
vis[r] = true;
while(!q.empty()){
int now = q.front();
for(int i=0;i<num;i++){
if(vis[i]==true;G[i][now]<INF)
{q.push(i);
vis[i] = true;}
}
}
}
可以看到,区别于DFS,BFS算法是没有递归的。
遍历图G:
void BFSTrave(){
for(int i=0;i<num;i++){
if(vis[i]==false)
BFS(i);
}
}
同样可以计算有几个连通块。
最短路径
Dijkstra算法
给定图G和起点s,通过算法得到S到达其他每个顶点的最短距离。而Dijkstra算法的基本思想是对图G(V,E)设置集合S,存放已被访问的顶点,然后每次从集合V-S中选择与起点s的最短距离最小的一个顶点(记为u),访问并加入集合S。令顶点u为中介点,优化起点s与所有从u能到达的顶点v之间的最短距离。操作n次(n为顶点个数),直到集合S包含所有顶点。
需要设置全局变量:
#define INF 1000000
#define maxn 1000
//必须
int n;//n个城市
int vis[maxn];//是否访问
int G[maxn][maxn];//两点之间距离
int dis[maxn];//起点到各点最短距离
//非必须,根据题目
//比如有些要求在有多条路径最短线路时选择某指标最高的路径,比如有些需要输出具体路径
int val[maxn];//起点到各点可得最高指标
int num[maxn];//各点某指标
int pre[maxn];//起点到该点最短路径上的前一个顶点
具体算法:
void Dirkstra(int s)//s为起点
{
for(int i=0;i<n;i++){
dis[i] = G[s][i];
}
dis[s] = 0;
for(int i=0;i<n;i++){
int k=-1, min = INF;
for(int j=0;j<n;j++){
if(vis[j]==0&&dis[j]<min)
{ min = dis[j]; k=j;}
}
if(k==-1) return;
vis[k] == 1;
for(int j=0;j<n;j++){
if(vis[j]==0 && G[k][j] != INF)
{ if(dis[j]>dis[k]+G[k][j]) {
dis[j] = dis[k]+G[k][j];
val[j] = val[k] + num[j];
pre[j] = k;
}
else if(dis[j]==dis[k]+G[k][j]){
if(val[j] < val[k] + num[j]){
val[j] = val[k] + num[j];
pre[j] = k;
}
}
}
}
}
}
上面把路径也存储了。如果要读取路径,可以用DFS算法进行递归:
void path_DFS(int s, int v)//s为起点,v为当前编号(从终点开始递归)
{
if(v==s) {
printf("%d\n", v);
return;}
path_DFS(s, pre[v]);
printf("%d\n", v);
}
可以在Dirkstra算法中也可以在main函数中设置全局变量:
memset(G,INF,sizeof(G));
memset(vis,0,sizeof(vis));
memset(dis,INF,sizeof(dis));//这个可以不设置
最小路径算法除了Dirkstra,还有Bellman-Ford算法,SPFA算法,Floyd算法等。Bellman-Ford算法可以处理负边权的问题,而Dirkstra不可以。
最小生成树
最小生成树(MST)是在一个给定的无向图G(V,E)中求一棵树,使得这棵树拥有图G中所有顶点,且所有边都是来自图G中的边,并且满足整棵树的边权之和最小。
最小生成树的性质:
- 边数等于顶点数减一,树内没有环
- 对给定图G(V,E),最小生成树不唯一,但其边权之和唯一
- 根结点可以是树上任意一个结点
最短路径与最小生成树的区别在于:
最短路径是找到两个点之间的最短路径,最小生成树是找到能把若干个离散点连起来的最短路径。
prim算法
基本思想:对图G(V,E)设置集合S,存放已被访问的顶点,然后每次从集合V-S中选择与集合S的最短距离最小的一个顶点(记为u),访问并加入集合S。令顶点u为中介点,优化所有从u能到达的顶点v与集合S之间的最短距离。操作执行n次(n为顶点个数),直到集合S已包含所有顶点。
设置全局变量:
#define maxn 1000
#define INF 1000000
int n;//n个离散点
int G[maxn][maxn];
int dis[maxn];
int vis[maxn];
prim算法:
int prim(){//默认0号为初始点,函数返回最小生成树的边权之和
fill(dis,dis+maxn,INF);
dis[0] = 0;
int ans = 0;//存放最小生成树的边权之和
for(int i=0;i<n;i++){
int k=-1,min=INF;
//查找未被访问的离集合S最近的点
for(int j=0;j<n;j++){
if(vis[j]==0&&dis[j]<min){
min = dis[j];
k=j;
}
}
if(k==-1) return;
vis[k] = 1;
ans += dis[k];
for(int j=0;j<n;j++){
if(vis[j]==0 && G[k][j] != INF && dis[j]>G[k][j]) //为了让该点到集合S更近
{ dis[j] = G[k][j]; }
}
}
return ans;
}
区别于Dirkstra算法,prim是找到距离集合S最近的点,优化也是为了到集合S最近,而非到初始点最近。
Kruskal
最小生成树还可以用Kruskal算法来解决。需要注意的是,稠密图用prim算法,稀疏图用kruskal算法。可以根据题目所给出的数据来进行选择。