最小生成树
最小生成树 (Minimum Spanning Tree, MST) 是指在一个无向连通图中找到一个树,使得该树包含图中所有顶点,并且边的权值之和最小。常用的算法有Prim算法和Kruskal算法。
Prim算法
Prim算法是一种贪心算法,从一个顶点开始,每次选择与当前生成树相连的权值最小的边,直到生成树包含所有顶点为止。
以下是Prim算法的C++代码:
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int MAXN = 1005;
int graph[MAXN][MAXN];
int dist[MAXN];
bool visited[MAXN];
int prim(int n) {
int minCost = 0;
memset(dist, INF, sizeof(dist));
memset(visited, false, sizeof(visited));
dist[1] = 0;
for (int i = 1; i <= n; i++) {
int u = -1;
for (int j = 1; j <= n; j++) {
if (!visited[j] && (u == -1 || dist[j] < dist[u])) {
u = j;
}
}
visited[u] = true;
minCost += dist[u];
for (int v = 1; v <= n; v++) {
if (!visited[v] && graph[u][v] < dist[v]) {
dist[v] = graph[u][v];
}
}
}
return minCost;
}
Kruskal算法
Kruskal算法是一种基于并查集的贪心算法,它按照边的权值从小到大的顺序选择边,并且保证选中的边不会构成环。
以下是Kruskal算法的C++代码:
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1005;
struct Edge {
int u, v, weight;
bool operator<(const Edge& other) const {
return weight < other.weight;
}
};
int parent[MAXN];
vector<Edge> edges;
int findParent(int x) {
if (parent[x] == x) {
return x;
}
return parent[x] = findParent(parent[x]);
}
int kruskal(int n) {
int minCost = 0;
sort(edges.begin(), edges.end());
for (int i = 1; i <= n; i++) {
parent[i] = i;
}
for (int i = 0; i < edges.size(); i++) {
int u = edges[i].u;
int v = edges[i].v;
int weight = edges[i].weight;
int parentU = findParent(u);
int parentV = findParent(v);
if (parentU != parentV) {
parent[parentU] = parentV;
minCost += weight;
}
}
return minCost;
}
单源最短路
单源最短路 (Single Source Shortest Path, SSSP) 是指在图中找到从一个源点到其它所有顶点的最短路径。常用的算法有Dijkstra算法和Bellman-Ford算法。
Dijkstra算法
Dijkstra算法是一种贪心算法,从源点开始,每次选择与当前顶点距离最短的顶点,并更新其它顶点的最短距离。
以下是Dijkstra算法的C++代码:
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int MAXN = 1005;
int graph[MAXN][MAXN];
int dist[MAXN];
bool visited[MAXN];
void dijkstra(int n, int source) {
memset(dist, INF, sizeof(dist));
memset(visited, false, sizeof(visited));
dist[source] = 0;
for (int i = 1; i <= n; i++) {
int u = -1;
for (int j = 1; j <= n; j++) {
if (!visited[j] && (u == -1 || dist[j] < dist[u])) {
u = j;
}
}
visited[u] = true;
for (int v = 1; v <= n; v++) {
if (!visited[v] && graph[u][v] != INF && dist[u] + graph[u][v] < dist[v]) {
dist[v] = dist[u] + graph[u][v];
}
}
}
}
Bellman-Ford算法
Bellman-Ford算法是一种动态规划算法,通过对所有边进行n-1轮松弛操作,可以找到从源点到其它所有顶点的最短路径。
以下是Bellman-Ford算法的C++代码:
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int MAXN = 1005;
struct Edge {
int u, v, weight;
};
int dist[MAXN];
vector<Edge> edges;
void bellmanFord(int n, int source) {
memset(dist, INF, sizeof(dist));
dist[source] = 0;
for (int i = 1; i <= n - 1; i++) {
for (int j = 0; j < edges.size(); j++) {
int u = edges[j].u;
int v = edges[j].v;
int weight = edges[j].weight;
if (dist[u] != INF && dist[u] + weight < dist[v]) {
dist[v] = dist[u] + weight;
}
}
}
}
建图方式
建图方式通常有两种:邻接矩阵和邻接表。
邻接矩阵
邻接矩阵是一个二维数组,其中的元素表示两个顶点之间的边的权值。
以下是使用邻接矩阵建图的C++代码示例:
const int MAXN = 1005;
const int INF = 0x3f3f3f3f;
int graph[MAXN][MAXN];
void addEdge(int u, int v, int weight) {
graph[u][v] = weight;
graph[v][u] = weight; // 如果是有向图,则不需要这一行
}
void initGraph(int n) {
memset(graph, INF, sizeof(graph));
}
邻接表
邻接表是一种链表的形式,其中的每个节点表示一个顶点,每个节点的邻居节点表示与该顶点相连的边。
以下是使用邻接表建图的C++代码示例:
const int MAXN = 1005;
const int INF = 0x3f3f3f3f;
struct Node {
int v, weight;
};
vector<Node> graph[MAXN];
void addEdge(int u, int v, int weight) {
graph[u].push_back({v, weight});
graph[v].push_back({u, weight}); // 如果是有向图,则不需要这一行
}
void initGraph(int n) {
for (int i = 1; i <= n; i++) {
graph[i].clear();
}
}
Floyd算法
Floyd算法是一种动态规划算法,通过对所有顶点之间的路径进行松弛操作,可以找到所有顶点之间的最短路径。
以下是Floyd算法的C++代码:
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int MAXN = 1005;
int dist[MAXN][MAXN];
void floyd(int n) {
for (int k = 1; k <= n; k++) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (dist[i][k] != INF && dist[k][j] != INF && dist[i][k] + dist[k][j] < dist[i][j]) {
dist[i][j] = dist[i][k] + dist[k][j];
}
}
}
}
}
传递闭包
传递闭包是指在一个有向图中,对于任意两个顶点u和v,如果存在一条从u到v的路径,则称u可以到达v。传递闭包可以用于判断两个顶点之间是否存在路径。
以下是传递闭包的C++代码:
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1005;
bool reach[MAXN][MAXN];
void transitiveClosure(int n) {
for (int k = 1; k <= n; k++) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
reach[i][j] = reach[i][j] || (reach[i][k] && reach[k][j]);
}
}
}
}
希望以上内容能对你有所帮助!