图论基本概念
完全图: 每对顶点之间有边并且只有唯一的一条边.
强连通分量:有向图中任意2点都联通的最大子图.
图的储存
邻接矩阵:也就是一个二维数组,a[i][j]的值代表是否相连.
适用范围:
1.稠密图
2.无多重边
3.数据规模小
链式前向星:(模拟链表)
代码实现(主要部分)
1 void add_eage(int from,int to,int dis) 2 { 3 eage[++num].next=head[from]; 4 eage[num].to=to; 5 eage[num].dis=dis; 6 head[from]=num; 7 }
割点&割边:
如果删去,那么原来联通的图就会变成2个或2个以上子图
判断割点:开一个low数组来记录i及i的子孙相连的最高的祖先的访问时间戳
low[u]=min(low[u],low[v]);
判断割边:当且仅当low[v]>dfn[u];
强连通分量Tarjan
1 void tarjan(int i)//开始搜索... 2 { 3 int j; 4 dfn[i]=low[i]=++times;//记录时间戳 5 stak[++stp]=i;//压入栈 6 for(j=head[i];j;j=a[j].next) 7 { 8 int k=a[j].to; 9 if(!dfn[j]) 10 { 11 tarjan(j); 12 if(low[j]<low[i]) low[i]=low[j]; 13 } 14 else if(instak[j]&&dfn[j]<low[i]) low[i]=dfn[j]; 15 } 16 if(dfn[i]==low[i])//判断改点是否为根节点 17 { 18 cnt++;//定义在最外面 19 do 20 { 21 j=stak[stop--]; 22 instak[j]=false; 23 belong[j]=cnt; 24 }while(i!=j); 25 } 26 } 27 //cnt代表多少个强连通分量
最短路
1.Dijkstra:不含负权
思路:每次找到离源点最近的一个点进行扩展,最终得到源点到其余所有点的最短路径,不含负权
2.Bellman-ford:有负权,不含负权回路
这个就转变成对边的松弛
for(int i=2; i<=n; i++) 24 { 25 int mindist = MAXINT; 26 int u = v0; // 找出当前未使用的点j的dist[j]最小值 27 for(int j=1; j<=n; ++j) 28 if((!S[j]) && dist[j]<mindist) 29 { 30 u = j; // u保存当前邻接点中距离最小的点的号码 31 mindist = dist[j]; 32 } 33 S[u] = true; 34 for(int j=1; j<=n; j++) 35 if((!S[j]) && A[u][j]<MAXINT) 36 { 37 if(dist[u] + A[u][j] < dist[j]) //在通过新加入的u点路径找到离v0点更短的路径 38 { 39 dist[j] = dist[u] + A[u][j]; //更新dist 40 prev[j] = u; //记录前驱顶点 41 } 42 } 43 }
3.SPFA:kuai
1 while (!Q.empty()) 2 { 3 int u = Q.front(); 4 Q.pop(); 5 visited[u] = 0; 6 for (int v = 0; v < vertex_num; v++) 7 { 8 if (matrix[u][v] != INT_MAX) //u与v直接邻接 9 { 10 if (dist[u] + matrix[u][v] < dist[v]) 11 { 12 dist[v] = dist[u] + matrix[u][v]; 13 path[v] = u; 14 if (!visited[v]) 15 { 16 Q.push(v); 17 enqueue_num[v]++; 18 if (enqueue_num[v] >= vertex_num) 19 return false; 20 visited[v] = 1; 21 } 22 } 23 } 24 } 25 }
最小生成树太简单了