# 常用代码模板 3—— 搜索与图论
# 树与图的存储
树是一种特殊的图,与图的存储方式相同。
对于无向图中的边 ab,存储两条有向边 a->b, b->a。
因此我们可以只考虑有向图的存储。
(1) 邻接矩阵:g [a][b] 存储边 a->b
(2) 邻接表:
1 2 3 4 5 6 7 8 9 10 11 12 int h[N], e[N], ne[N], idx;void add (int a, int b) { e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ; } idx = 0 ; memset (h, -1 , sizeof h);
# 树与图的遍历
时间复杂度 O ( n + m ) O(n+m) O ( n + m ) ,n n n 表示点数,m m m 表示边数
(1) 深度优先遍历
1 2 3 4 5 6 7 8 9 10 int dfs (int u) { st[u] = true ; for (int i = h[u]; i != -1 ; i = ne[i]) { int j = e[i]; if (!st[j]) dfs (j); } }
(2) 宽度优先遍历
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 queue<int > q; st[1 ] = true ; q.push (1 ); while (q.size ()){ int t = q.front (); q.pop (); for (int i = h[t]; i != -1 ; i = ne[i]) { int j = e[i]; if (!st[j]) { st[j] = true ; q.push (j); } } }
# 拓扑排序
时间复杂度 O ( n + m ) O(n+m) O ( n + m ) ,n n n 表示点数,m m m 表示边数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 bool topsort () { int hh = 0 , tt = -1 ; for (int i = 1 ; i <= n; i ++ ) if (!d[i]) q[ ++ tt] = i; while (hh <= tt) { int t = q[hh ++ ]; for (int i = h[t]; i != -1 ; i = ne[i]) { int j = e[i]; if (-- d[j] == 0 ) q[ ++ tt] = j; } } return tt == n - 1 ; }
# 朴素 dijkstra 算法
时间复杂度 O ( n 2 + m ) O(n^2+m) O ( n 2 + m ) ,n n n 表示点数,m m m 表示边数
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 int g[N][N]; int dist[N]; bool st[N]; int dijkstra () { memset (dist, 0x3f , sizeof dist); dist[1 ] = 0 ; for (int i = 0 ; i < n - 1 ; i ++ ) { int t = -1 ; for (int j = 1 ; j <= n; j ++ ) if (!st[j] && (t == -1 || dist[t] > dist[j])) t = j; for (int j = 1 ; j <= n; j ++ ) dist[j] = min (dist[j], dist[t] + g[t][j]); st[t] = true ; } if (dist[n] == 0x3f3f3f3f ) return -1 ; return dist[n]; }
# 堆优化版 Dijkstra
时间复杂度 O ( m l o g n ) O(mlogn) O ( m l o g n ) ,n n n 表示点数,m m m 表示边数
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 typedef pair<int , int > PII;int n; int h[N], w[N], e[N], ne[N], idx; int dist[N]; bool st[N]; int dijkstra () { memset (dist, 0x3f , sizeof dist); dist[1 ] = 0 ; priority_queue<PII, vector<PII>, greater<PII>> heap; heap.push ({0 , 1 }); while (heap.size ()) { auto t = heap.top (); heap.pop (); int ver = t.second, distance = t.first; if (st[ver]) continue ; st[ver] = true ; for (int i = h[ver]; i != -1 ; i = ne[i]) { int j = e[i]; if (dist[j] > distance + w[i]) { dist[j] = distance + w[i]; heap.push ({dist[j], j}); } } } if (dist[n] == 0x3f3f3f3f ) return -1 ; return dist[n]; }
# Bellman-Ford 算法
时间复杂度 O ( n m ) O(nm) O ( n m ) ,n n n 表示点数,m m m 表示边数
注意在模板题中需要对下面的模板稍作修改,加上备份数组,详情见模板题。
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 int n, m; int dist[N]; struct Edge { int a, b, w; }edges[M]; int bellman_ford () { memset (dist, 0x3f , sizeof dist); dist[1 ] = 0 ; for (int i = 0 ; i < n; i ++ ) { for (int j = 0 ; j < m; j ++ ) { int a = edges[j].a, b = edges[j].b, w = edges[j].w; if (dist[b] > dist[a] + w) dist[b] = dist[a] + w; } } if (dist[n] > 0x3f3f3f3f / 2 ) return -1 ; return dist[n]; }
# spfa 算法(队列优化的 Bellman-Ford 算法)
时间复杂度 平均情况下O ( m ) O(m) O ( m ) ,最坏情况下O ( n m ) O(nm) O ( n m ) ,n n n 表示点数,m m m 表示边数
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 int n; int h[N], w[N], e[N], ne[N], idx; int dist[N]; bool st[N]; int spfa () { memset (dist, 0x3f , sizeof dist); dist[1 ] = 0 ; queue<int > q; q.push (1 ); st[1 ] = true ; while (q.size ()) { auto t = q.front (); q.pop (); st[t] = false ; for (int i = h[t]; i != -1 ; i = ne[i]) { int j = e[i]; if (dist[j] > dist[t] + w[i]) { dist[j] = dist[t] + w[i]; if (!st[j]) { q.push (j); st[j] = true ; } } } } if (dist[n] == 0x3f3f3f3f ) return -1 ; return dist[n]; }
# spfa 判断图中是否存在负环
时间复杂度 O ( n m ) O(nm) O ( n m ) ,n n n 表示点数,m m m 表示边数
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 int n; int h[N], w[N], e[N], ne[N], idx; int dist[N], cnt[N]; bool st[N]; bool spfa () { queue<int > q; for (int i = 1 ; i <= n; i ++ ) { q.push (i); st[i] = true ; } while (q.size ()) { auto t = q.front (); q.pop (); st[t] = false ; for (int i = h[t]; i != -1 ; i = ne[i]) { int j = e[i]; if (dist[j] > dist[t] + w[i]) { dist[j] = dist[t] + w[i]; cnt[j] = cnt[t] + 1 ; if (cnt[j] >= n) return true ; if (!st[j]) { q.push (j); st[j] = true ; } } } } return false ; }
# floyd 算法
时间复杂度 O ( n 3 ) O(n^3) O ( n 3 ) ,n n n 表示点数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 初始化: for (int i = 1 ; i <= n; i ++ ) for (int j = 1 ; j <= n; j ++ ) if (i == j) d[i][j] = 0 ; else d[i][j] = INF; void floyd () { for (int k = 1 ; k <= n; k ++ ) for (int i = 1 ; i <= n; i ++ ) for (int j = 1 ; j <= n; j ++ ) d[i][j] = min (d[i][j], d[i][k] + d[k][j]); }
# 朴素版 prim 算法
时间复杂度 O ( n 2 + m ) O(n^2+m) O ( n 2 + m ) ,n n n 表示点数,m m m 表示边数
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; int g[N][N]; int dist[N]; bool st[N]; int prim () { memset (dist, 0x3f , sizeof dist); int res = 0 ; for (int i = 0 ; i < n; i ++ ) { int t = -1 ; for (int j = 1 ; j <= n; j ++ ) if (!st[j] && (t == -1 || dist[t] > dist[j])) t = j; if (i && dist[t] == INF) return INF; if (i) res += dist[t]; st[t] = true ; for (int j = 1 ; j <= n; j ++ ) dist[j] = min (dist[j], g[t][j]); } return res; }
# Kruskal 算法
时间复杂度 O ( m l o g m ) O(mlogm) O ( m l o g m ) ,n n n 表示点数,m m m 表示边数
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 int n, m; int p[N]; struct Edge { int a, b, w; bool operator < (const Edge &W)const { return w < W.w; } }edges[M]; int find (int x) { if (p[x] != x) p[x] = find (p[x]); return p[x]; } int kruskal () { sort (edges, edges + m); for (int i = 1 ; i <= n; i ++ ) p[i] = i; int res = 0 , cnt = 0 ; for (int i = 0 ; i < m; i ++ ) { int a = edges[i].a, b = edges[i].b, w = edges[i].w; a = find (a), b = find (b); if (a != b) { p[a] = b; res += w; cnt ++ ; } } if (cnt < n - 1 ) return INF; return res; }
# 染色法判别二分图
时间复杂度 O ( n + m ) O(n+m) O ( n + m ) ,n n n 表示点数,m m m 表示边数
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; int h[N], e[M], ne[M], idx; int color[N]; bool dfs (int u, int c) { color[u] = c; for (int i = h[u]; i != -1 ; i = ne[i]) { int j = e[i]; if (color[j] == -1 ) { if (!dfs (j, !c)) return false ; } else if (color[j] == c) return false ; } return true ; } bool check () { memset (color, -1 , sizeof color); bool flag = true ; for (int i = 1 ; i <= n; i ++ ) if (color[i] == -1 ) if (!dfs (i, 0 )) { flag = false ; break ; } return flag; }
# 匈牙利算法
时间复杂度 O ( n m ) O(nm) O ( n m ) ,n n n 表示点数,m m m 表示边数
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 int n1, n2; int h[N], e[M], ne[M], idx; int match[N]; bool st[N]; bool find (int x) { for (int i = h[x]; i != -1 ; i = ne[i]) { int j = e[i]; if (!st[j]) { st[j] = true ; if (match[j] == 0 || find (match[j])) { match[j] = x; return true ; } } } return false ; } int res = 0 ;for (int i = 1 ; i <= n1; i ++ ){ memset (st, false , sizeof st); if (find (i)) res ++ ; }