早起的鸟儿有虫吃
图的遍历 * 有两种存储方式:邻接矩阵和邻接表
* 在一些顶点数目比较大(一般顶点个数在1000以上)的情况下,使用邻接表而
不是邻接矩阵来存储图。如果是稀疏图,用邻接表,如果是稠密图,用邻接矩阵。
深度优先搜索dfs遍历图 1 2 3 4 5 6 7 8 9 10 11 12 dfs(u) { vis[u] = true ; for (从u出发到能到达的所有顶点v) if (vis[v] == false ) dfs(v); } dfsTravel(G) { for (G的所有顶点u) if (vis[u] == false ) dfs(u); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 int n, G[maxV][maxV];bool vis[maxV] = {false };void dfs (int u, int depth) { vis[u] = true ; for (int v = 0 ; v < n; v++) if (G[u][v] != INF && vis[v] == false ) dfs(v, depth + 1 ); } void travelDFS () { for (int u = 0 ; u < n; u++) if (vis[u] == false ) dfs(u, 1 ); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 vector <int > arr[maxn];void dfs (int u, int depth) { vis[u] = true ; for (int i = 0 ; i < arr[u].size(); i++){ int v = arr[u][i]; if (vis[i] == false ) dfs(v, depth + 1 ); } } void dfsTrave () { for (int u = 0 ; u < n; u++) { if (vis[u] == false ) dfs(u, 1 ); } }
广度优先搜索bfs遍历图 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 bfs(u) { queue q; 将u入队 inq[u] = true ; while (q非空) { for (从u出发到可到达的所有顶点v) { if (inq[v] == false ) 将v入队 inq[v] = true ; } } } bfsTrave(G) { for (G的所有顶点u) { if (inq[u] == false ) bfs(u); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 int n, G[maxV][maxV];bool inq[maxV] = {false };void bfs (int u) { queue <int > q; q.push(u); inq[u] = true ; while (!q.empty()) { int u = q.front(); q.pop(); for (int v = 0 ; v < n; v++) if (G[u][v] != INF && inq[v] == false ){ q.push(v);、 inq[v] = true ; } } } void travelBFS () { for (int v = 0 ; v < n; v++) if (inq[v] == false ) bfs(v); }
1 2 3 4 5 6 7 8 struct node { int v; int layer; }; next.layer = curNode.layer + 1 ; vector <node> Adj[N];
最短路径 * 单源最短路径:计算源点到其他各顶点的最短路径的长度
* 全局最短路径:图中任意两点的最短路径
* Dijkstra、Bellman-Ford、SPFA求单源最短路径
* Floyd-Warshall可以求全局最短路径,但是效率比较低
* SPFA算法是Bellman-Ford算法的队列优化
* Dijkstra算法不能求带负权边的最短路径,而SPFA算法、Bellman-Ford算法、Floyd-Warshall可以求带负权边的最短路径
* Bellman-Ford算法的核心代码只有4行,Floyd-Warshall算法的核心代码只有5行
* 深度优先遍历可以求一个点到另一个点的最短路径的长度
单源最短路径 思想来源:图的广度优先遍历bfs
Dijkstra 算法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 int G[maxSize][maxSize], n;int d[maxSize];int pre[maxSize];bool vis[maxSize] = {false };void Dijkstra (int s) { fill(d, d + maxSize, INF); d[s] = 0 ; for (int i = 0 ; i < n; i++){ int u = -1 , min = INF; for (int j = 0 ; j < n; j++) if (vis[j] == false && d[j] < min) { u = j; min = d[j]; } if (u == -1 ) return ; vis[u] = true ; for (int v = 0 ; v < n; v++) { if (G[u][v] != INF && vis[v] == false && d[u] + G[u][v] < d[v]) { d[v] = d[u] + G[u][v]; pre[v] = u; } } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 struct Node { int v, dis; }; vector <Node> Adj[MAXV];for (int j = 0 ; j < Adj[u].size(); j++) { int v = Adj[u][j].v; if (vis[v] == false && d[u] + Adj[u][v].dis < d[v]) { d[v] = d[u] + Adj[u][v].dis; } }
1 2 3 4 5 6 7 8 9 10 void DFS (int s, int v) { if (v == s) { printf ("%d\n" , s); return ; } DFS(s, pre[v]); printf ("%d\n" , v); }
附加考法:第一标尺是距离,第二标尺常见有三种
①新增边权:给每条边再增加一个边权(比如花费),然后要求在最短路径有多条时要求路径上的花费之和最小(如果边权是其它含义,也可以是最大)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 for (int v = 0 ; v < n; v++) { if (G[u][v] != INF && vis[v] == false ) { if (d[u] + G[u][v] < d[v]) { d[v] = d[u] + G[u][v]; c[v] = c[u] +cost[u][v]; } else if (d[u] + G[u][v] == d[v] && c[u] +cost[u][v] < c[v]){ c[v] = c[u] +cost[u][v]; } } }
②新增点权:给每个点增加一个点权(例如每个城市能收集到的物资),然后在最短路径
有多条时要求路径上的点权之和最大(如果点权是其它含义也可以是最小)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 for (int v = 0 ; v < n; v++) { if (G[u][v] != INF && vis[v] == false ) { if (d[u] + G[u][v] < d[v]) { d[v] = d[u] + G[u][v]; w[v] = w[u] + weight[v]; } else if (d[u] + G[u][v] == d[v] && w[u] + weight[v] > w[v]){ w[v] = w[u] + weight[v]; } } }
③直接问有多少条最短路径
1 2 3 4 5 6 7 8 9 10 11 12 13 for (int v = 0 ; v < n; v++) { if (G[u][v] != INF && vis[v] == false ) { if (d[u] + G[u][v] < d[v]) { d[v] = d[u] + G[u][v]; num[v] = num[u]; } else if (d[u] + G[u][v] == d[v]){ num[v] += num[u]; } } }
Bellman-Ford 算法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 struct Node { int v, dis; }; vector <Node> Adj[MAXV]; int n, d[MAXV]; bool Bellman (int s) { fill(s, d + MAXV, INF); d[s] = 0 ; for (int i = 0 ; i < n - 1 ; i++) { for (u = 0 ; u < n; u++){ for (int j = 0 ; j < Adj[u].size(); j++) { int v = Adj[u][j].v; int dis = Adj[u][j].dis; if (d[u] + dis < d[v]) { d[v] = d[u] + dis; } } } } for (int u = 0 ; u < n; u++) { for (int j = 0 ; j < Adj[u].size(); j++) { int v = Adj[u][j].v; int dis = Adj[u][j].dis; if (d[u] + dis < d[v]) { return false ; } } } return true ; }
SPFA 算法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 queue <int > q;源点s入队 while (队列非空) { 取出队首元素 for (u的所有邻接边u->v) { if (d[u] + dis < d[v]) { d[v] = d[u] + dis; if (v当前不在队列) { v入队; if (v入队次数大于n - 1 ) { 说明有可达负环,return ; } } } } }
全源最短路径 Floyd 算法 1 2 3 4 5 枚举顶点 k ∈ [1 ,n] 以顶点 k 作为中介点,枚举所有顶点对i和j (i,j∈ [1 ,n]) 如果dis[i][k] + dis[k][j] < dis[i][j]成立 赋值dis[i][j] = dis[i][k] + dis[k][j]
1 2 3 4 5 6 7 8 9 10 11 12 13 int n, m; int dis[MAXV][MAXV]; 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[i][k] != INF && dis[k][j] != INF && dis[i][k] + dis[k][j] < dis[i][j]) dis[i][j] = dis[i][k] + dis[k][j]; } } } }
最小生成树 * 如果是稠密图(边多) prim算法
* 如果是稀疏图(边少) kruskal算法
prim 算法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Prim(G, d[]){ 初始化 for (循环n次){ u = 使d[u]最小的还未被访问的顶点的标号 for (从u除法能到达的所有顶点){ if (v未被访问&& 以u为中介点使得v与集合S的最短距离d[v]更优){ 将G[u][v]赋值给v与集合S的最短距离d[v] } } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 int n, G[MAXV][MAXV]; int d[MAXV]; bool vis[MAXV] = {false }; int prim () { fill(d, d + MAXV, INF); d[0 ] = 0 ; int ans = 0 ; for (int i = 0 ; i < n; i++) { int u = -1 , min = INF; for (int j = 0 ; j < n; j++) { if (vis[j] == false && d[j] < min) { u = j; min = d[j]; } } if (u == -1 ) return -1 ; vis[u] = true ; ans += d[u]; for (int v = 0 ; v < n; v++) { if (G[u][v] != INF && vis[v] ==false && G[u][v] < d[v]){ d[v] = G[u][v]; } } } return ans; }
1 2 3 4 5 6 7 8 9 10 11 12 13 struct Node { int v, dis; }; vector <Node> Adj[MAXV]for (int j = 0 ; j < Adj[u].size(); j++) { int v = Adj[u][j].v; if (vis[v] == false && Adj[u][j].dis < d[v]) { d[v] = Adj[u][v].dis; } }
kruskal 算法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 struct edge { int u, v; int cost; }E[MAXE]; int kruskal () { 令最小生成树的边权之和为ans,最小生成树的当前边数为Num_Edge; 将所有边按边权从小到大排序; for (从小到大枚举所有边){ if (当前测试边的两个端点在不同的连通块中) { 将该测试边加入最小生成树中; ans += 测试边边权; 最小生成树的当前边数Num_Edge 加 1 ; 当边数Num_Edge等于顶点数减1 时结束循环; } } return ans; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 int father[N];int findFather (int x) { while (x != father[x]) x = father[x]; return x; } bool cmp (edge a, edge b) { return a.cost < b.cost; } int kruskal (int n, int m) { int ans = 0 ,Num_Edge = 0 ; for (int i = 1 ; i <= n; i++) { father[i] = i; } sort(E, E + m, cmp); for (int i = 0 ; i < m; i++) { int faU = findFather(E[i].u); int faV = findFather(E[i].v); if (faU != faV) { father[faU] = faV; ans += E[i].cost; Num_Edge++; if (Num_Edge == n - 1 ) break ; } } if (Num_Edge != n - 1 ) return -1 ; else return ans; }
拓扑排序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 vector <int > G[MAXV]; int n, inDegree[MAXV];bool topologicalSort () { queue <int > q; int num = 0 ; for (int i = 0 ; i < n; i++) { if (inDegree[i] == 0 ) { q.push(i); } } while (!q.empty()) { int u = q.front(); q.pop(); for (int i = 0 ; i < G[u].size(); i++) { int v = G[u][i]; inDegree[v]--; if (inDegree[v] == 0 ) { q.push(v); } } G[u].clear(); num++; } if (num == n) return true ; else return false ; }
关键路径 AOV网 和 AOE网 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
最长路径算法
关键路径算法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 struct node { int v, w; }; vector <node> G[MAXV]; stack <int > topOrder; bool topologicalSort () { queue <int > q; for (int i= 0 ; i < n; i++) { if (inDegree[i] == 0 ) { q.push(i); } } while (!q.empty()) { int u = q.front(); q.pop(); topOrder.push(u); for (int i = 0 ; i < G[u].size(); i++) { int v = G[u][i].v; inDegree[v]--; if (inDegree[v] == 0 ) { q.push(v); } if (ve[u] + G[u][v].w > ve[v]) { ve[v] = ve[u] + G[u][v].w; } } } if (topOrder.size() == n) return true ; else return false ; } fill(vl,vl + n, ve[n - 1 ]); while (!topOrder.empty()) { int u = topOrder.top(); topOrder.pop(); for (int i = 0 ; i < G[u].size(); i++) { int v = G[u][i].v; if (vl[v] - G[u][i].w < vl[u]) { vl[u] = vl[v] - G[u][i].w; } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 int criticalPath () { menset(ve, 0 , sizeof (ve)); if (topologicalSort() == false ) { return -1 ; } fill(vl,vl + n, ve[n - 1 ]); while (!topOrder.empty()) { int u = topOrder.top(); topOrder.pop(); for (int i = 0 ; i < G[u].size(); i++) { int v = G[u][i].v; if (vl[v] - G[u][i].w < vl[u]) { vl[u] = vl[v] - G[u][i].w; } } } for (int u = 0 ; u < n; u++) { for (int i = 0 ; i < G[u][i].size(); i++) { int v = G[u][i].v, w = G[u][i].w; int e = ve[u], l = vl[v] - w; if (e == l) { printf ("%d->%d\n" , u, v); } } } return ve[n - 1 ]; }
<
大学阅读记录
debug
>