搜索与图论
树和图的存储
树是一种特殊的图,与图的存储方式相同。
对于无向图中的边ab,存储两条有向边a->b, b->a。
因此我们可以只考虑有向图的存储。
邻接矩阵:g[a][b] 存储边a->b
邻接表 (前向星):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 const int N = 1e5 +10 ;struct Node { int to, ne, w; }node[N*2 ]; int head[N], idx;void addEdge (int a, int b, int w) { node[idx].to = b; node[idx].ne = head[a]; node[idx].w = w; head[a] = idx++; } idx = 0 ; memset (head, -1 , sizeof (head));
树与图的遍历
时间复杂度 O(n+m) n 表示点数, m表示边数
深度优先遍历
1 2 3 4 5 6 7 8 9 10 11 12 13 int vis[N]; void dfs (int root) { vis[root] = true ; for (int i = head[root]; i!=-1 ; i = node[i].ne){ int j = node[i].to; if (!vis[j]) dfs (j); } }
广度优先搜索
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 queue<int > que; vis[1 ] = true ; q.push (1 ); while (que.size ()){ int cur = que.front (); que.pop (); for (int i = head[cur]; i != -1 ; i = node[i].ne){ int j = node[i].to; if (!vis[j]){ vis[j] = true ; que.push (j); } } }
拓扑排序
时间复杂度 O(n+m), n表示点数,m表示边数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 queue<int > que; for (int i=0 ;i<n; i++) if (d[i] == 0 ) que.push (i); while (!que.empty ()){ int cur = que.front (); que.pop (); for (int i = head[cur]; i != -1 ; i = node[i].ne){ int j = node[i].to; if (-- d[j] == 0 ) que.push (j); } }
最短路径
Dijkstra求最短路径
单源最短路径
时间复杂度 O(mlogn),n点数,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 typedef pair<int , int > PII;struct Node { int to, ne, w; }node[N]; int head[N], idx;int dis[N], vis[N];int dijkstra (int a, int b) { memset (dis, 0x3f , sizeof (dis)); dis[a] = 0 ; priority_queue<PII, vector<PII>, greater<PII>> heap; heap.push ({0 , a}); while (heap.size ()){ auto t = heap.top (); heap.pop (); int cur = t.second; if (vis[cur]) continue ; vis[cur] = 1 ; for (int i = head[cur]; i!=-1 ; i=node[i].ne){ int j = node[i].to; if (dis[j] > dis[cur] + node[i].w){ dis[j] = dis[cur] + node[i].w; heap.push ({dis[j], j}); } } } if (dis[b] == 0x3f3f3f3f ) return -1 ; return dis[b]; }
Bellman-Ford算法
时间复杂度 O(nm), n表示点数,m表示边数
Spfa
算法(队列优化的Bellman-Ford算法)
时间复杂度 平均情况下 O(m),最坏情况下 O(nm),
n表示点数,m表示边数
Spfa判断图中是否存在负环
时间复杂度是 O(nm), n表示点数,m表示边数
Floyd算法
多元最短路径
时间复杂度是 O(n3), n表示点数
1 2 3 4 5 6 7 8 9 10 11 12 13 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算法
最小生成树,类似Dijkstra算法
堆优化版本,时间复杂度是 O(mlogn),n表示点数,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 int dis[N], vis[N];int n, m;int prim () { memset (dis, 0x3f , sizeof (dis)); int sum = 0 , cnt = 0 ; priority_queue<pll, vector<pll>, greater<pll>> heap; dis[1 ] = 0 ; heap.push ({0 , 1 }); while (!heap.empty ()){ auto cur = heap.top (); heap.pop (); int distance = cur.first, a = cur.second; if (vis[a]) continue ; vis[a] = 1 ; sum += distance; cnt++; for (int i = head[a]; i!=-1 ; i = node[i].ne){ int b = node[i].to; if (!vis[b] && dis[b] > node[i].w){ dis[b] = node[i].w; heap.push ({dis[b], b}); } } } if (cnt != n) return INF; else return sum; }
Kruskal算法
时间复杂度是 O(mlogm), n表示点数,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 int n, m; int p[N]; struct Edge { int a, b, w; bool operator <(const Edge &e) const { return w < e.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; }
二分图
染色法
匈牙利算法